diff --git a/vsfilter/Copy.cpp b/vsfilter/Copy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bcb269faebd9ecbb9cbe51aff641a3a065483722
--- /dev/null
+++ b/vsfilter/Copy.cpp
@@ -0,0 +1,561 @@
+// Copyright 2003-2006 Gabest
+// http://www.gabest.org
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
+// http://www.gnu.org/copyleft/gpl.html
+
+#include "stdafx.h"
+#include <math.h>
+#include "DirectVobSubFilter.h"
+#include "dsutil/DSUtil.h"
+#include "dsutil/MediaTypes.h"
+
+#include <initguid.h>
+#include "include/moreuuids.h"
+
+extern int c2y_yb[256];
+extern int c2y_yg[256];
+extern int c2y_yr[256];
+extern void ColorConvInit();
+
+void BltLineRGB32(DWORD* d, BYTE* sub, int w, const GUID& subtype)
+{
+	if(subtype == MEDIASUBTYPE_YV12 || subtype == MEDIASUBTYPE_I420 || subtype == MEDIASUBTYPE_IYUV)
+	{
+		BYTE* db = (BYTE*)d;
+		BYTE* dbtend = db + w;
+
+		for(; db < dbtend; sub+=4, db++)
+		{
+			if(sub[3] < 0xff)
+			{
+				int y = (c2y_yb[sub[0]] + c2y_yg[sub[1]] + c2y_yr[sub[2]] + 0x108000) >> 16; 
+				*db = y; // w/o colors 
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_YUY2)
+	{
+		WORD* ds = (WORD*)d;
+		WORD* dstend = ds + w;
+
+		for(; ds < dstend; sub+=4, ds++)
+		{
+			if(sub[3] < 0xff)
+			{
+				int y = (c2y_yb[sub[0]] + c2y_yg[sub[1]] + c2y_yr[sub[2]] + 0x108000) >> 16; 
+				*ds = 0x8000|y; // w/o colors 
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_RGB555)
+	{
+		WORD* ds = (WORD*)d;
+		WORD* dstend = ds + w;
+
+		for(; ds < dstend; sub+=4, ds++)
+		{
+			if(sub[3] < 0xff)
+			{
+				*ds = ((*((DWORD*)sub)>>9)&0x7c00)|((*((DWORD*)sub)>>6)&0x03e0)|((*((DWORD*)sub)>>3)&0x001f);
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_RGB565)
+	{
+		WORD* ds = (WORD*)d;
+		WORD* dstend = ds + w;
+
+		for(; ds < dstend; sub+=4, ds++)
+		{
+			if(sub[3] < 0xff)
+			{
+				*ds = ((*((DWORD*)sub)>>8)&0xf800)|((*((DWORD*)sub)>>5)&0x07e0)|((*((DWORD*)sub)>>3)&0x001f);
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_RGB24)
+	{
+		BYTE* dt = (BYTE*)d;
+		BYTE* dstend = dt + w*3;
+
+		for(; dt < dstend; sub+=4, dt+=3)
+		{
+			if(sub[3] < 0xff)
+			{
+				dt[0] = sub[0];
+				dt[1] = sub[1];
+				dt[2] = sub[2];
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_RGB32 || subtype == MEDIASUBTYPE_ARGB32)
+	{
+		DWORD* dstend = d + w;
+
+		for(; d < dstend; sub+=4, d++)
+		{
+			if(sub[3] < 0xff) *d = *((DWORD*)sub)&0xffffff;
+		}
+	}
+}
+
+/* ResX2 */
+void Scale2x(const GUID& subtype, BYTE* d, int dpitch, BYTE* s, int spitch, int w, int h)
+{
+	if(subtype == MEDIASUBTYPE_YV12 || subtype == MEDIASUBTYPE_I420 || subtype == MEDIASUBTYPE_IYUV)
+	{
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch) // TODO: replace this mess with mmx code
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			for(BYTE* s3 = s1 + (w-1); s1 < s3; s1 += 1, d1 += 2)
+			{
+				d1[0] = s1[0]; 
+				d1[1] = (s1[0]+s1[1])>>1;
+			}
+
+			d1[0] = d1[1] = s1[0]; 
+
+			s1 += 1;
+			d1 += 2;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines8(d, h*2, dpitch);
+	}
+	else if(subtype == MEDIASUBTYPE_YUY2)
+	{
+		unsigned __int64 __0xffffffff00000000 = 0xffffffff00000000;
+		unsigned __int64 __0x00000000ffffffff = 0x00000000ffffffff;
+		unsigned __int64 __0x00ff00ff00ff00ff = 0x00ff00ff00ff00ff;
+
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch)
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			// row0, 4 pixels: y1|u1|y2|v1|y3|u2|y4|v2
+			// ->
+			// row0, 8 pixels: y1|u1|(y1+y2)/2|v1|y2|(u1+u2)/2|(y2+y3)/2|(v1+v2)/2
+
+			__asm
+			{
+				mov		esi, s1
+				mov		edi, d1
+
+				mov		ecx, w
+				shr		ecx, 1
+				dec		ecx
+
+				movq	mm4, __0x00ff00ff00ff00ff
+				movq	mm5, __0x00000000ffffffff
+				movq	mm6, __0xffffffff00000000
+row_loop1:
+				movq	mm0, [esi]
+				movq	mm2, mm0
+
+				pand	mm0, mm4	// mm0 = 00y400y300y200y1
+				psrlw	mm2, 8		// mm2 = 00u200v200u100v1
+
+
+				movq	mm1, mm0
+
+				pand	mm0, mm5	// mm0 = 0000000000y200y1
+
+				psllq	mm1, 16
+				pand	mm1, mm6	// mm1 = 00y300y200000000
+
+				por		mm1, mm0	// mm1 = 00y300y200y200y1
+
+				punpcklwd mm0, mm0	// mm0 = 00y200y200y100y1
+
+				paddw	mm0, mm1
+				psrlw	mm0, 1		// mm0 = (mm0 + mm1) / 2
+
+
+				movq	mm1, mm2
+				punpckldq	mm1, mm1 // mm1 = 00u100v100u100v1
+
+				paddw	mm1, mm2
+				psrlw	mm1, 1		// mm1 = (mm1 + mm2) / 2
+
+
+				psllw	mm1, 8
+				por		mm0, mm1	// mm0 = (v1+v2)/2|(y2+y3)/2|(u1+u2)/2|y2|v1|(y1+y2)/2|u1|y1
+
+				movq	[edi], mm0
+
+				lea		esi, [esi+4]
+				lea		edi, [edi+8]
+
+				dec		ecx
+				jnz		row_loop1
+
+				mov		s1, esi
+				mov		d1, edi
+			};
+
+			*d1++ = s1[0];
+			*d1++ = s1[1];
+			*d1++ =(s1[0]+s1[2])>>1;
+			*d1++ = s1[3];
+
+			*d1++ = s1[2];
+			*d1++ = s1[1];
+			*d1++ = s1[2];
+			*d1++ = s1[3];
+
+			s1 += 4;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines8(d, h*2, dpitch);
+	}
+	else if(subtype == MEDIASUBTYPE_RGB555)
+	{
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch) // TODO: replace this mess with mmx code
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			for(BYTE* s3 = s1 + (w-1)*2; s1 < s3; s1 += 2, d1 += 4)
+			{
+				*((WORD*)d1) = *((WORD*)s1);
+				*((WORD*)d1+1) = 
+					((((*((WORD*)s1)&0x7c00) + (*((WORD*)s1+1)&0x7c00)) >> 1)&0x7c00)|
+					((((*((WORD*)s1)&0x03e0) + (*((WORD*)s1+1)&0x03e0)) >> 1)&0x03e0)|
+					((((*((WORD*)s1)&0x001f) + (*((WORD*)s1+1)&0x001f)) >> 1)&0x001f);
+			}
+
+			*((WORD*)d1) = *((WORD*)s1);
+			*((WORD*)d1+1) = *((WORD*)s1);
+
+			s1 += 2;
+			d1 += 4;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines555(d, h*2, dpitch);
+	}
+	else if(subtype == MEDIASUBTYPE_RGB565)
+	{
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch) // TODO: replace this mess with mmx code
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			for(BYTE* s3 = s1 + (w-1)*2; s1 < s3; s1 += 2, d1 += 4)
+			{
+				*((WORD*)d1) = *((WORD*)s1);
+				*((WORD*)d1+1) = 
+					((((*((WORD*)s1)&0xf800) + (*((WORD*)s1+1)&0xf800)) >> 1)&0xf800)|
+					((((*((WORD*)s1)&0x07e0) + (*((WORD*)s1+1)&0x07e0)) >> 1)&0x07e0)|
+					((((*((WORD*)s1)&0x001f) + (*((WORD*)s1+1)&0x001f)) >> 1)&0x001f);
+			}
+
+			*((WORD*)d1) = *((WORD*)s1);
+			*((WORD*)d1+1) = *((WORD*)s1);
+
+			s1 += 2;
+			d1 += 4;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines565(d, h*2, dpitch);
+	}
+	else if(subtype == MEDIASUBTYPE_RGB24)
+	{
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch) // TODO: replace this mess with mmx code
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			for(BYTE* s3 = s1 + (w-1)*3; s1 < s3; s1 += 3, d1 += 6)
+			{
+				d1[0] = s1[0]; 
+				d1[1] = s1[1]; 
+				d1[2] = s1[2];
+				d1[3] = (s1[0]+s1[3])>>1;
+				d1[4] = (s1[1]+s1[4])>>1;
+				d1[5] = (s1[2]+s1[5])>>1;
+			}
+
+			d1[0] = d1[3] = s1[0]; 
+			d1[1] = d1[4] = s1[1]; 
+			d1[2] = d1[5] = s1[2];
+
+			s1 += 3;
+			d1 += 6;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines8(d, h*2, dpitch);
+	}
+	else if(subtype == MEDIASUBTYPE_RGB32 || subtype == MEDIASUBTYPE_ARGB32)
+	{
+		BYTE* s1;
+		BYTE* s2;
+		BYTE* d1;
+
+		for(s1 = s, s2 = s + h*spitch, d1 = d; s1 < s2; d1 += dpitch)
+		{
+			BYTE* stmp = s1 + spitch;
+			BYTE* dtmp = d1 + dpitch;
+
+			__asm
+			{
+				mov		esi, s1
+				mov		edi, d1
+
+				mov		ecx, w
+				dec		ecx
+
+				pxor	mm0, mm0
+row_loop3:
+				movq	mm1, [esi]
+				movq	mm2, mm1
+
+				punpcklbw mm1, mm0	// mm1 = 00xx00r100g100b1
+				punpckhbw mm2, mm0	// mm2 = 00xx00r200g200b2
+
+				paddw	mm2, mm1
+				psrlw	mm2, 1		// mm2 = (mm1 + mm2) / 2
+
+				packuswb	mm1, mm2
+
+				movq	[edi], mm1
+
+				lea		esi, [esi+4]
+				lea		edi, [edi+8]
+
+				dec		ecx
+				jnz		row_loop3
+
+				mov		s1, esi
+				mov		d1, edi
+			};
+
+			*((DWORD*)d1) = *((DWORD*)s1);
+			*((DWORD*)d1+1) = *((DWORD*)s1);
+
+			s1 += 4;
+			d1 += 8;
+
+			s1 = stmp;
+			d1 = dtmp;
+		}
+
+		AvgLines8(d, h*2, dpitch);
+	}
+
+	__asm emms;
+}
+
+HRESULT CDirectVobSubFilter::Copy(BYTE* pSub, BYTE* pIn, CSize sub, CSize in, int bpp, const GUID& subtype, DWORD black)
+{
+	int wIn = in.cx, hIn = in.cy, pitchIn = wIn*bpp>>3;
+	int wSub = sub.cx, hSub = sub.cy, pitchSub = wSub*bpp>>3;
+	bool fScale2x = wIn*2 <= wSub;
+
+	if(fScale2x) wIn <<= 1, hIn <<= 1;
+
+	int left = ((wSub - wIn)>>1)&~1;
+	int mid = wIn;
+	int right = left + ((wSub - wIn)&1);
+
+	int dpLeft = left*bpp>>3;
+	int dpMid = mid*bpp>>3;
+	int dpRight = right*bpp>>3;
+
+	ASSERT(wSub >= wIn);
+
+	{
+		int i = 0, j = 0;
+
+		j += (hSub - hIn) >> 1;
+
+		for(; i < j; i++, pSub += pitchSub)
+		{
+			memsetd(pSub, black, dpLeft+dpMid+dpRight);
+		}
+
+		j += hIn;
+
+		if(hIn > hSub)
+			pIn += pitchIn * ((hIn - hSub) >> (fScale2x?2:1));
+
+		if(fScale2x)
+		{
+			Scale2x(subtype, 
+				pSub + dpLeft, pitchSub, pIn, pitchIn, 
+				in.cx, (min(j, hSub) - i) >> 1);
+            
+			for(int k = min(j, hSub); i < k; i++, pIn += pitchIn, pSub += pitchSub)
+			{
+				memsetd(pSub, black, dpLeft);
+				memsetd(pSub + dpLeft+dpMid, black, dpRight);
+			}
+		}
+		else
+		{
+			for(int k = min(j, hSub); i < k; i++, pIn += pitchIn, pSub += pitchSub)
+			{
+				memsetd(pSub, black, dpLeft);
+				memcpy(pSub + dpLeft, pIn, dpMid);
+				memsetd(pSub + dpLeft+dpMid, black, dpRight);
+			}
+		}
+
+		j = hSub;
+
+		for(; i < j; i++, pSub += pitchSub)
+		{
+			memsetd(pSub, black, dpLeft+dpMid+dpRight);
+		}
+	}
+
+	return NOERROR;
+}
+
+void CDirectVobSubFilter::PrintMessages(BYTE* pOut)
+{
+	if(!m_hdc || !m_hbm)
+		return;
+
+	ColorConvInit();
+
+	const GUID& subtype = m_pOutput->CurrentMediaType().subtype;
+
+	BITMAPINFOHEADER bihOut;
+	ExtractBIH(&m_pOutput->CurrentMediaType(), &bihOut);
+
+	CString msg, tmp;
+
+	if(m_fOSD)
+	{
+		tmp.Format(_T("in: %dx%d %s\nout: %dx%d %s\n"), 
+			m_w, m_h, 
+			Subtype2String(m_pInput->CurrentMediaType().subtype),
+			bihOut.biWidth, bihOut.biHeight, 
+			Subtype2String(m_pOutput->CurrentMediaType().subtype));
+		msg += tmp;
+
+		tmp.Format(_T("real fps: %.3f, current fps: %.3f\nmedia time: %d, subtitle time: %d [ms]\nframe number: %d (calculated)\nrate: %.4f\n"), 
+			m_fps, m_fMediaFPSEnabled?m_MediaFPS:fabs(m_fps),
+			(int)m_tPrev.Millisecs(), (int)(CalcCurrentTime()/10000),
+			(int)(m_tPrev.m_time * m_fps / 10000000),
+			m_pInput->CurrentRate());
+		msg += tmp;
+
+		CAutoLock cAutoLock(&m_csQueueLock);
+
+		if(m_pSubPicQueue)
+		{
+			int nSubPics = -1;
+			REFERENCE_TIME rtNow = -1, rtStart = -1, rtStop = -1;
+			m_pSubPicQueue->GetStats(nSubPics, rtNow, rtStart, rtStop);
+			tmp.Format(_T("queue stats: %I64d - %I64d [ms]\n"), rtStart/10000, rtStop/10000);
+			msg += tmp;
+
+			for(int i = 0; i < nSubPics; i++)
+			{
+				m_pSubPicQueue->GetStats(i, rtStart, rtStop);
+				tmp.Format(_T("%d: %I64d - %I64d [ms]\n"), i, rtStart/10000, rtStop/10000);
+				msg += tmp;
+			}
+
+		}
+	}
+
+	if(msg.IsEmpty()) return;
+
+	HANDLE hOldBitmap = SelectObject(m_hdc, m_hbm);
+	HANDLE hOldFont = SelectObject(m_hdc, m_hfont);
+
+	SetTextColor(m_hdc, 0xffffff);
+	SetBkMode(m_hdc, TRANSPARENT);
+	SetMapMode(m_hdc, MM_TEXT);
+
+	BITMAP bm;
+	GetObject(m_hbm, sizeof(BITMAP), &bm);
+
+	CRect r(0, 0, bm.bmWidth, bm.bmHeight);
+	DrawText(m_hdc, msg, _tcslen(msg), &r, DT_CALCRECT|DT_EXTERNALLEADING|DT_NOPREFIX|DT_WORDBREAK);
+
+	r += CPoint(10, 10);
+	r &= CRect(0, 0, bm.bmWidth, bm.bmHeight);
+
+	DrawText(m_hdc, msg, _tcslen(msg), &r, DT_LEFT|DT_TOP|DT_NOPREFIX|DT_WORDBREAK);
+
+	BYTE* pIn = (BYTE*)bm.bmBits;
+	int pitchIn = bm.bmWidthBytes;
+	int pitchOut = bihOut.biWidth * bihOut.biBitCount >> 3;
+
+	if(subtype == MEDIASUBTYPE_YV12 || subtype == MEDIASUBTYPE_I420 || subtype == MEDIASUBTYPE_IYUV)
+		pitchOut = bihOut.biWidth;
+
+	pitchIn = (pitchIn+3)&~3;
+	pitchOut = (pitchOut+3)&~3;
+
+	if(bihOut.biHeight > 0 && bihOut.biCompression <= 3) // flip if the dst bitmap is flipped rgb (m_hbm is a top-down bitmap, not like the subpictures)
+	{
+		pOut += pitchOut * (abs(bihOut.biHeight)-1);
+		pitchOut = -pitchOut;
+	}
+
+	pIn += pitchIn * r.top;
+	pOut += pitchOut * r.top;
+
+	for(int w = min(r.right, m_w), h = r.Height(); h--; pIn += pitchIn, pOut += pitchOut)
+	{
+		BltLineRGB32((DWORD*)pOut, pIn, w, subtype);
+		memsetd(pIn, 0xff000000, r.right*4);
+	}
+
+	SelectObject(m_hdc, hOldBitmap);
+	SelectObject(m_hdc, hOldFont);
+}
diff --git a/vsfilter/DirectVobSub.cpp b/vsfilter/DirectVobSub.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af5b320db63399ed3acb6fca01c3efad3bb5b36d
--- /dev/null
+++ b/vsfilter/DirectVobSub.cpp
@@ -0,0 +1,629 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "DirectVobSub.h"
+#include "VSFilter.h"
+
+CDirectVobSub::CDirectVobSub()
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	BYTE* pData;
+	UINT nSize;
+
+	m_iSelectedLanguage = 0;
+	m_fHideSubtitles = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_HIDE), 0);
+	m_fDoPreBuffering = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_DOPREBUFFERING), 1);
+	m_fOverridePlacement = !!theApp.GetProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_OVERRIDEPLACEMENT), 0);
+	m_PlacementXperc = theApp.GetProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_XPERC), 50);
+	m_PlacementYperc = theApp.GetProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_YPERC), 90);
+	m_fBufferVobSub = !!theApp.GetProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_BUFFER), 1);
+	m_fOnlyShowForcedVobSubs = !!theApp.GetProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_ONLYSHOWFORCEDSUBS), 0);
+	m_fPolygonize = !!theApp.GetProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_POLYGONIZE), 0);
+	m_defStyle <<= theApp.GetProfileString(ResStr(IDS_R_TEXT), ResStr(IDS_RT_STYLE), _T(""));
+	m_fFlipPicture = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FLIPPICTURE), 0);
+	m_fFlipSubtitles = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FLIPSUBTITLES), 0);
+	m_fOSD = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SHOWOSDSTATS), 0);
+	m_fSaveFullPath = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SAVEFULLPATH), 0);
+	m_nReloaderDisableCount = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_DISABLERELOADER), 0) ? 1 : 0;
+	m_SubtitleDelay = theApp.GetProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLEDELAY), 0);
+	m_SubtitleSpeedMul = theApp.GetProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLESPEEDMUL), 1000);
+	m_SubtitleSpeedDiv = theApp.GetProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLESPEEDDIV), 1000);
+	m_fMediaFPSEnabled = !!theApp.GetProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_MEDIAFPSENABLED), 0);
+	pData = NULL;
+	if(theApp.GetProfileBinary(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_MEDIAFPS), &pData, &nSize) && pData)
+	{
+		if(nSize != sizeof(m_MediaFPS)) m_MediaFPS = 25.0;
+		else memcpy(&m_MediaFPS, pData, sizeof(m_MediaFPS));
+		delete [] pData;
+	}
+	m_ZoomRect.left = m_ZoomRect.top = 0;
+	m_ZoomRect.right = m_ZoomRect.bottom = 1;
+
+	m_fForced = false;
+}
+
+CDirectVobSub::~CDirectVobSub()
+{
+	CAutoLock cAutoLock(&m_propsLock);
+}
+
+STDMETHODIMP CDirectVobSub::get_FileName(WCHAR* fn)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!fn) return E_POINTER;
+
+#ifdef UNICODE
+	wcscpy(fn, m_FileName);
+#else
+	mbstowcs(fn, m_FileName, m_FileName.GetLength()+1);
+#endif
+
+    return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_FileName(WCHAR* fn)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!fn) return E_POINTER;
+
+	CString tmp = fn;
+	if(!m_FileName.Left(m_FileName.ReverseFind('.')+1).CompareNoCase(tmp.Left(tmp.ReverseFind('.')+1))) return S_FALSE;
+
+#ifdef UNICODE
+	m_FileName = fn;
+#else
+	CHARSETINFO cs={0};
+	::TranslateCharsetInfo((DWORD *)DEFAULT_CHARSET, &cs, TCI_SRCCHARSET);
+	CHAR* buff = m_FileName.GetBuffer(MAX_PATH*2);
+	int len = WideCharToMultiByte(cs.ciACP/*CP_OEMCP*/, NULL, fn, -1, buff, MAX_PATH*2, NULL, NULL);
+	m_FileName.ReleaseBuffer(len+1);
+#endif
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_LanguageCount(int* nLangs)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return nLangs ? *nLangs = 0, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::get_LanguageName(int iLanguage, WCHAR** ppName)
+{
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_SelectedLanguage(int* iSelected)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return iSelected ? *iSelected = m_iSelectedLanguage, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::put_SelectedLanguage(int iSelected)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_iSelectedLanguage == iSelected) return S_FALSE;
+
+	int nCount;
+	if(FAILED(get_LanguageCount(&nCount))
+	|| iSelected < 0 
+	|| iSelected >= nCount) 
+		return E_FAIL;
+
+	m_iSelectedLanguage = iSelected;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_HideSubtitles(bool* fHideSubtitles)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return fHideSubtitles ? *fHideSubtitles = m_fHideSubtitles, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::put_HideSubtitles(bool fHideSubtitles)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fHideSubtitles == fHideSubtitles) return S_FALSE;
+
+	m_fHideSubtitles = fHideSubtitles;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_PreBuffering(bool* fDoPreBuffering)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return fDoPreBuffering ? *fDoPreBuffering = m_fDoPreBuffering, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::put_PreBuffering(bool fDoPreBuffering)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fDoPreBuffering == fDoPreBuffering) return S_FALSE;
+
+	m_fDoPreBuffering = fDoPreBuffering;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_Placement(bool* fOverridePlacement, int* xperc, int* yperc)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fOverridePlacement) *fOverridePlacement = m_fOverridePlacement;
+	if(xperc) *xperc = m_PlacementXperc;
+	if(yperc) *yperc = m_PlacementYperc;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_Placement(bool fOverridePlacement, int xperc, int yperc)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fOverridePlacement == fOverridePlacement && m_PlacementXperc == xperc && m_PlacementYperc == yperc) return S_FALSE;
+
+	m_fOverridePlacement = fOverridePlacement;
+	m_PlacementXperc = xperc;
+	m_PlacementYperc = yperc;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_VobSubSettings(bool* fBuffer, bool* fOnlyShowForcedSubs, bool* fPolygonize)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fBuffer) *fBuffer = m_fBufferVobSub;
+	if(fOnlyShowForcedSubs) *fOnlyShowForcedSubs = m_fOnlyShowForcedVobSubs;
+	if(fPolygonize) *fPolygonize = m_fPolygonize;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fPolygonize)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fBufferVobSub == fBuffer && m_fOnlyShowForcedVobSubs == fOnlyShowForcedSubs && m_fPolygonize == fPolygonize) return S_FALSE;
+
+	m_fBufferVobSub = fBuffer;
+	m_fOnlyShowForcedVobSubs = fOnlyShowForcedSubs;
+	m_fPolygonize = fPolygonize;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_TextSettings(void* lf, int lflen, COLORREF* color, bool* fShadow, bool* fOutline, bool* fAdvancedRenderer)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(lf)
+	{
+		if(lflen == sizeof(LOGFONTA))
+			strncpy_s(((LOGFONTA*)lf)->lfFaceName, LF_FACESIZE, CStringA(m_defStyle.fontName), _TRUNCATE);
+		else if(lflen == sizeof(LOGFONTW))
+			wcsncpy_s(((LOGFONTW*)lf)->lfFaceName, LF_FACESIZE, CStringW(m_defStyle.fontName), _TRUNCATE);
+		else
+			return E_INVALIDARG;
+
+		((LOGFONT*)lf)->lfCharSet = m_defStyle.charSet;
+		((LOGFONT*)lf)->lfItalic = m_defStyle.fItalic;
+		((LOGFONT*)lf)->lfHeight = m_defStyle.fontSize;
+		((LOGFONT*)lf)->lfWeight = m_defStyle.fontWeight;
+		((LOGFONT*)lf)->lfStrikeOut = m_defStyle.fStrikeOut;
+		((LOGFONT*)lf)->lfUnderline = m_defStyle.fUnderline;
+	}
+
+	if(color) *color = m_defStyle.colors[0];
+	if(fShadow) *fShadow = m_defStyle.shadowDepth>0;
+	if(fOutline) *fOutline = m_defStyle.outlineWidth>0;
+	if(fAdvancedRenderer) *fAdvancedRenderer = m_fAdvancedRenderer;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(lf)
+	{
+		if(lflen == sizeof(LOGFONTA))
+			m_defStyle.fontName = ((LOGFONTA*)lf)->lfFaceName;
+		else if(lflen == sizeof(LOGFONTW))
+			m_defStyle.fontName = ((LOGFONTW*)lf)->lfFaceName;
+		else
+			return E_INVALIDARG;
+
+		m_defStyle.charSet = ((LOGFONT*)lf)->lfCharSet;
+		m_defStyle.fItalic = !!((LOGFONT*)lf)->lfItalic;
+		m_defStyle.fontSize = ((LOGFONT*)lf)->lfHeight;
+		m_defStyle.fontWeight = ((LOGFONT*)lf)->lfWeight;
+		m_defStyle.fStrikeOut = !!((LOGFONT*)lf)->lfStrikeOut;
+		m_defStyle.fUnderline = !!((LOGFONT*)lf)->lfUnderline;
+
+		if(m_defStyle.fontSize < 0)
+		{
+			HDC hdc = ::GetDC(0);
+			m_defStyle.fontSize = -m_defStyle.fontSize * 72 / GetDeviceCaps(hdc, LOGPIXELSY);
+			::ReleaseDC(0, hdc);
+		}
+
+	}
+
+	m_defStyle.colors[0] = color;
+	m_defStyle.shadowDepth = fShadow?2:0;
+	m_defStyle.outlineWidth = fOutline?2:0;
+
+	return S_OK;
+
+}
+
+STDMETHODIMP CDirectVobSub::get_Flip(bool* fPicture, bool* fSubtitles)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fPicture) *fPicture = m_fFlipPicture;
+	if(fSubtitles) *fSubtitles = m_fFlipSubtitles;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_Flip(bool fPicture, bool fSubtitles)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fFlipPicture == fPicture && m_fFlipSubtitles == fSubtitles) return S_FALSE;
+
+	m_fFlipPicture = fPicture;
+	m_fFlipSubtitles = fSubtitles;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_OSD(bool* fOSD)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return fOSD ? *fOSD = m_fOSD, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::put_OSD(bool fOSD)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fOSD == fOSD) return S_FALSE;
+
+	m_fOSD = fOSD;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_SaveFullPath(bool* fSaveFullPath)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	return fSaveFullPath ? *fSaveFullPath = m_fSaveFullPath, S_OK : E_POINTER;
+}
+
+STDMETHODIMP CDirectVobSub::put_SaveFullPath(bool fSaveFullPath)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fSaveFullPath == fSaveFullPath) return S_FALSE;
+
+	m_fSaveFullPath = fSaveFullPath;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_SubtitleTiming(int* delay, int* speedmul, int* speeddiv)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(delay) *delay = m_SubtitleDelay;
+	if(speedmul) *speedmul = m_SubtitleSpeedMul;
+	if(speeddiv) *speeddiv = m_SubtitleSpeedDiv;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_SubtitleTiming(int delay, int speedmul, int speeddiv)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_SubtitleDelay == delay && m_SubtitleSpeedMul == speedmul && m_SubtitleSpeedDiv == speeddiv) return S_FALSE;
+
+	m_SubtitleDelay = delay;
+	m_SubtitleSpeedMul = speedmul;
+	if(speeddiv > 0) m_SubtitleSpeedDiv = speeddiv;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_MediaFPS(bool* fEnabled, double* fps)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fEnabled) *fEnabled = m_fMediaFPSEnabled;
+	if(fps) *fps = m_MediaFPS;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_MediaFPS(bool fEnabled, double fps)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(m_fMediaFPSEnabled == fEnabled && m_MediaFPS == fps) return S_FALSE;
+
+	m_fMediaFPSEnabled = fEnabled;
+	if(fps > 0) m_MediaFPS = fps;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_ZoomRect(NORMALIZEDRECT* rect)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!rect) return E_POINTER;
+
+	*rect = m_ZoomRect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_ZoomRect(NORMALIZEDRECT* rect)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!rect) return E_POINTER;
+
+	if(!memcmp(&m_ZoomRect, rect, sizeof(m_ZoomRect))) return S_FALSE;
+
+	m_ZoomRect = *rect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::UpdateRegistry()
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_HIDE), m_fHideSubtitles);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_DOPREBUFFERING), m_fDoPreBuffering);
+	theApp.WriteProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_OVERRIDEPLACEMENT), m_fOverridePlacement);
+	theApp.WriteProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_XPERC), m_PlacementXperc);
+	theApp.WriteProfileInt(ResStr(IDS_R_TEXT), ResStr(IDS_RT_YPERC), m_PlacementYperc);
+	theApp.WriteProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_BUFFER), m_fBufferVobSub);
+	theApp.WriteProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_ONLYSHOWFORCEDSUBS), m_fOnlyShowForcedVobSubs);
+	theApp.WriteProfileInt(ResStr(IDS_R_VOBSUB), ResStr(IDS_RV_POLYGONIZE), m_fPolygonize);
+	CString style;
+	theApp.WriteProfileString(ResStr(IDS_R_TEXT), ResStr(IDS_RT_STYLE), style <<= m_defStyle);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FLIPPICTURE), m_fFlipPicture);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FLIPSUBTITLES), m_fFlipSubtitles);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SHOWOSDSTATS), m_fOSD);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SAVEFULLPATH), m_fSaveFullPath);
+	theApp.WriteProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLEDELAY), m_SubtitleDelay);
+	theApp.WriteProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLESPEEDMUL), m_SubtitleSpeedMul);
+	theApp.WriteProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_SUBTITLESPEEDDIV), m_SubtitleSpeedDiv);
+	theApp.WriteProfileInt(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_MEDIAFPSENABLED), m_fMediaFPSEnabled);
+	theApp.WriteProfileBinary(ResStr(IDS_R_TIMING), ResStr(IDS_RTM_MEDIAFPS), (BYTE*)&m_MediaFPS, sizeof(m_MediaFPS));
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::HasConfigDialog(int iSelected)
+{
+	return E_NOTIMPL;
+}
+
+STDMETHODIMP CDirectVobSub::ShowConfigDialog(int iSelected, HWND hWndParent)
+{
+	return E_NOTIMPL;
+}
+
+STDMETHODIMP CDirectVobSub::IsSubtitleReloaderLocked(bool* fLocked)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!fLocked) return E_POINTER; 
+
+	bool fDisabled;
+	get_SubtitleReloader(&fDisabled);
+
+	*fLocked = fDisabled || m_nReloaderDisableCount > 0;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::LockSubtitleReloader(bool fLock)
+{
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fLock) m_nReloaderDisableCount++;
+	else m_nReloaderDisableCount--;
+
+	ASSERT(m_nReloaderDisableCount >= 0);
+	if(m_nReloaderDisableCount < 0) m_nReloaderDisableCount = 0;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_SubtitleReloader(bool* fDisabled)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(fDisabled) *fDisabled = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_DISABLERELOADER), 0);
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_SubtitleReloader(bool fDisable)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	bool b;
+	get_SubtitleReloader(&b);
+	if(b == fDisable) return S_FALSE;
+
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_DISABLERELOADER), fDisable);
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_ExtendPicture(int* horizontal, int* vertical, int* resx2, int* resx2minw, int* resx2minh)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(horizontal) *horizontal = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_MOD32FIX), 0) & 1;
+	if(vertical) *vertical = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EXTPIC), 1);
+	if(resx2) *resx2 = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2), 2) & 3;
+	if(resx2minw) *resx2minw = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2MINW), 384);
+	if(resx2minh) *resx2minh = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2MINH), 288);
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_ExtendPicture(int horizontal, int vertical, int resx2, int resx2minw, int resx2minh)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	int i[5];
+	get_ExtendPicture(i, i+1, i+2, i+3, i+4);
+	if(i[0] == horizontal && i[1] == vertical && i[2] == resx2 && i[3] == resx2minw && i[4] == resx2minh) return S_FALSE;
+
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_MOD32FIX), horizontal & 1);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EXTPIC), vertical);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2), resx2 & 3);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2MINW), resx2minw);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_RESX2MINH), resx2minh);
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_LoadSettings(int* level, bool* fExternalLoad, bool* fWebLoad, bool* fEmbeddedLoad)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(level) *level = theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_LOADLEVEL), 0) & 3;
+	if(fExternalLoad) *fExternalLoad = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EXTERNALLOAD), 1);
+	if(fWebLoad) *fWebLoad = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_WEBLOAD), 0);
+	if(fEmbeddedLoad) *fEmbeddedLoad = !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EMBEDDEDLOAD), 1);
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_LoadSettings(int level, bool fExternalLoad, bool fWebLoad, bool fEmbeddedLoad)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	int i;
+	bool b[3];
+	get_LoadSettings(&i, b, b+1, b+2);
+	if(i == level && b[0] == fExternalLoad && b[1] == fWebLoad && b[2] == fEmbeddedLoad) return S_FALSE;
+
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_LOADLEVEL), level & 3);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EXTERNALLOAD), fExternalLoad);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_WEBLOAD), fWebLoad);
+	theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_EMBEDDEDLOAD), fEmbeddedLoad);
+
+	return S_OK;
+}
+
+// IDirectVobSub2
+
+STDMETHODIMP CDirectVobSub::AdviseSubClock(ISubClock* pSubClock)
+{
+	m_pSubClock = pSubClock;
+	return S_OK;
+}
+
+STDMETHODIMP_(bool) CDirectVobSub::get_Forced()
+{
+	return m_fForced;
+}
+
+STDMETHODIMP CDirectVobSub::put_Forced(bool fForced)
+{
+	m_fForced = fForced;
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::get_TextSettings(STSStyle* pDefStyle)
+{
+	CheckPointer(pDefStyle, E_POINTER);
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	*pDefStyle = m_defStyle;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSub::put_TextSettings(STSStyle* pDefStyle)
+{
+	CheckPointer(pDefStyle, E_POINTER);
+
+	CAutoLock cAutoLock(&m_propsLock);
+
+	if(!memcmp(&m_defStyle, pDefStyle, sizeof(m_defStyle)))
+		return S_FALSE;
+
+	m_defStyle = *pDefStyle;
+
+	return S_OK;
+}
+
+// IFilterVersion
+
+STDMETHODIMP_(DWORD) CDirectVobSub::GetFilterVersion()
+{
+	return 0x0234;
+}
diff --git a/vsfilter/DirectVobSub.h b/vsfilter/DirectVobSub.h
new file mode 100644
index 0000000000000000000000000000000000000000..b1d9df9dc40d3255c8f053938e366592c2a488aa
--- /dev/null
+++ b/vsfilter/DirectVobSub.h
@@ -0,0 +1,124 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "IDirectVobSub.h"
+#include "include/IFilterVersion.h"
+
+class CDirectVobSub : public IDirectVobSub2, public IFilterVersion
+{
+protected:
+	CDirectVobSub();
+	virtual ~CDirectVobSub();
+
+protected:
+    CCritSec m_propsLock;
+
+	CString m_FileName;
+	int m_iSelectedLanguage;
+	bool m_fHideSubtitles;
+	bool m_fDoPreBuffering;
+	bool m_fOverridePlacement;
+	int	m_PlacementXperc, m_PlacementYperc;
+	bool m_fBufferVobSub, m_fOnlyShowForcedVobSubs, m_fPolygonize;
+
+	STSStyle m_defStyle;
+
+	bool m_fAdvancedRenderer;
+	bool m_fFlipPicture, m_fFlipSubtitles;
+	bool m_fOSD;
+	int m_nReloaderDisableCount;
+	int m_SubtitleDelay, m_SubtitleSpeedMul, m_SubtitleSpeedDiv;
+	bool m_fMediaFPSEnabled;
+	double m_MediaFPS;
+	bool m_fSaveFullPath;
+	NORMALIZEDRECT m_ZoomRect;
+
+	CComPtr<ISubClock> m_pSubClock;
+	bool m_fForced;
+
+public:
+
+	// IDirectVobSub
+
+    STDMETHODIMP get_FileName(WCHAR* fn);
+    STDMETHODIMP put_FileName(WCHAR* fn);
+	STDMETHODIMP get_LanguageCount(int* nLangs);
+	STDMETHODIMP get_LanguageName(int iLanguage, WCHAR** ppName);
+	STDMETHODIMP get_SelectedLanguage(int* iSelected);
+	STDMETHODIMP put_SelectedLanguage(int iSelected);
+	STDMETHODIMP get_HideSubtitles(bool* fHideSubtitles);
+    STDMETHODIMP put_HideSubtitles(bool fHideSubtitles);
+    STDMETHODIMP get_PreBuffering(bool* fDoPreBuffering);
+    STDMETHODIMP put_PreBuffering(bool fDoPreBuffering);
+    STDMETHODIMP get_Placement(bool* fOverridePlacement, int* xperc, int* yperc);
+    STDMETHODIMP put_Placement(bool fOverridePlacement, int xperc, int yperc);
+    STDMETHODIMP get_VobSubSettings(bool* fBuffer, bool* fOnlyShowForcedSubs, bool* fPolygonize);
+    STDMETHODIMP put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fPolygonize);
+    STDMETHODIMP get_TextSettings(void* lf, int lflen, COLORREF* color, bool* fShadow, bool* fOutline, bool* fAdvancedRenderer);
+    STDMETHODIMP put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer);
+    STDMETHODIMP get_Flip(bool* fPicture, bool* fSubtitles);
+    STDMETHODIMP put_Flip(bool fPicture, bool fSubtitles);
+    STDMETHODIMP get_OSD(bool* fShowOSD);
+    STDMETHODIMP put_OSD(bool fShowOSD);
+	STDMETHODIMP get_SaveFullPath(bool* fSaveFullPath);
+	STDMETHODIMP put_SaveFullPath(bool fSaveFullPath);
+    STDMETHODIMP get_SubtitleTiming(int* delay, int* speedmul, int* speeddiv);
+    STDMETHODIMP put_SubtitleTiming(int delay, int speedmul, int speeddiv);
+    STDMETHODIMP get_MediaFPS(bool* fEnabled, double* fps);
+    STDMETHODIMP put_MediaFPS(bool fEnabled, double fps);
+	STDMETHODIMP get_ZoomRect(NORMALIZEDRECT* rect);
+    STDMETHODIMP put_ZoomRect(NORMALIZEDRECT* rect);
+	STDMETHODIMP get_ColorFormat(int* iPosition) {return E_NOTIMPL;}
+    STDMETHODIMP put_ColorFormat(int iPosition) {return E_NOTIMPL;}
+
+	STDMETHODIMP UpdateRegistry();
+
+	STDMETHODIMP HasConfigDialog(int iSelected);
+	STDMETHODIMP ShowConfigDialog(int iSelected, HWND hWndParent);
+
+	// settings for the rest are stored in the registry
+
+	STDMETHODIMP IsSubtitleReloaderLocked(bool* fLocked);
+    STDMETHODIMP LockSubtitleReloader(bool fLock);
+	STDMETHODIMP get_SubtitleReloader(bool* fDisabled);
+    STDMETHODIMP put_SubtitleReloader(bool fDisable);
+
+	// the followings need a partial or full reloading of the filter
+
+	STDMETHODIMP get_ExtendPicture(int* horizontal, int* vertical, int* resx2, int* resx2minw, int* resx2minh);
+	STDMETHODIMP put_ExtendPicture(int horizontal, int vertical, int resx2, int resx2minw, int resx2minh);
+	STDMETHODIMP get_LoadSettings(int* level, bool* fExternalLoad, bool* fWebLoad, bool* fEmbeddedLoad);
+	STDMETHODIMP put_LoadSettings(int level, bool fExternalLoad, bool fWebLoad, bool fEmbeddedLoad);
+
+	// IDirectVobSub2
+
+	STDMETHODIMP AdviseSubClock(ISubClock* pSubClock);
+	STDMETHODIMP_(bool) get_Forced();
+	STDMETHODIMP put_Forced(bool fForced);
+    STDMETHODIMP get_TextSettings(STSStyle* pDefStyle);
+    STDMETHODIMP put_TextSettings(STSStyle* pDefStyle);
+
+	// IFilterVersion
+	
+	STDMETHODIMP_(DWORD) GetFilterVersion();
+};
\ No newline at end of file
diff --git a/vsfilter/DirectVobSubFilter.cpp b/vsfilter/DirectVobSubFilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d24532b0f46dd45f9a83330605040cc9b1f4796e
--- /dev/null
+++ b/vsfilter/DirectVobSubFilter.cpp
@@ -0,0 +1,1716 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <math.h>
+#include <time.h>
+#include "DirectVobSubFilter.h"
+#include "TextInputPin.h"
+#include "DirectVobSubPropPage.h"
+#include "VSFilter.h"
+#include "systray.h"
+#include "DSUtil/MediaTypes.h"
+#include "SubPic/MemSubPic.h"
+
+#include <initguid.h>
+#include "include/moreuuids.h"
+
+///////////////////////////////////////////////////////////////////////////
+
+/*removeme*/
+bool g_RegOK = true;//false; // doesn't work with the dvd graph builder
+#include "valami.cpp"
+
+////////////////////////////////////////////////////////////////////////////
+//
+// Constructor
+//
+
+CDirectVobSubFilter::CDirectVobSubFilter(LPUNKNOWN punk, HRESULT* phr, const GUID& clsid)
+	: CBaseVideoFilter(NAME("CDirectVobSubFilter"), punk, phr, clsid)
+	, m_nSubtitleId(-1)
+	, m_fMSMpeg4Fix(false)
+	, m_fDivxPlusFix(false)
+	, m_fps(25)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	m_hdc = 0;
+	m_hbm = 0;
+	m_hfont = 0;
+
+	{
+		LOGFONT lf;
+		memset(&lf, 0, sizeof(lf));
+		lf.lfCharSet = DEFAULT_CHARSET;
+		lf.lfOutPrecision = OUT_CHARACTER_PRECIS;
+		lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+		lf.lfQuality = ANTIALIASED_QUALITY;
+		HDC hdc = GetDC(NULL);
+		lf.lfHeight = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+		ReleaseDC(NULL, hdc);
+		lf.lfWeight = FW_BOLD;
+		_tcscpy(lf.lfFaceName, _T("Arial"));
+		m_hfont = CreateFontIndirect(&lf);
+	}
+
+	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Hint"), _T("The first three are fixed, but you can add more up to ten entries."));
+	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path0"), _T("."));
+	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path1"), _T("c:\\subtitles"));
+	theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), _T("Path2"), _T(".\\subtitles"));
+
+	m_fLoading = true;
+
+	m_hSystrayThread = 0;
+	m_tbid.hSystrayWnd = NULL;
+	m_tbid.graph = NULL;
+	m_tbid.fRunOnce = false;
+	m_tbid.fShowIcon = (theApp.m_AppName.Find(_T("zplayer"), 0) < 0 || !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_ENABLEZPICON), 0));
+
+	HRESULT hr = S_OK;
+	m_pTextInput.Add(new CTextInputPin(this, m_pLock, &m_csSubLock, &hr));
+	ASSERT(SUCCEEDED(hr));
+
+	CAMThread::Create();
+	m_frd.EndThreadEvent.Create(0, FALSE, FALSE, 0);
+	m_frd.RefreshEvent.Create(0, FALSE, FALSE, 0);
+}
+
+CDirectVobSubFilter::~CDirectVobSubFilter()
+{
+	CAutoLock cAutoLock(&m_csQueueLock);
+	if(m_pSubPicQueue) m_pSubPicQueue->Invalidate();
+	m_pSubPicQueue = NULL;
+
+	if(m_hfont) {DeleteObject(m_hfont); m_hfont = 0;}
+	if(m_hbm) {DeleteObject(m_hbm); m_hbm = 0;}
+	if(m_hdc) {DeleteObject(m_hdc); m_hdc = 0;}
+
+	for(int i = 0; i < m_pTextInput.GetCount(); i++) 
+		delete m_pTextInput[i];
+
+	m_frd.EndThreadEvent.Set();
+	CAMThread::Close();
+}
+
+STDMETHODIMP CDirectVobSubFilter::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+    CheckPointer(ppv, E_POINTER);
+
+    return 
+		QI(IDirectVobSub)
+		QI(IDirectVobSub2)
+		QI(IFilterVersion)
+		QI(ISpecifyPropertyPages)
+		QI(IAMStreamSelect)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// CBaseVideoFilter
+
+void CDirectVobSubFilter::GetOutputSize(int& w, int& h, int& arx, int& ary)
+{
+	CSize s(w, h), os = s;
+	AdjustFrameSize(s);
+	w = s.cx;
+	h = s.cy;
+
+	if(w != os.cx)
+	{
+		while(arx < 100) arx *= 10, ary *= 10;
+		arx = arx * w / os.cx;
+	}
+	
+	if(h != os.cy)
+	{
+		while(ary < 100) arx *= 10, ary *= 10;
+		ary = ary * h / os.cy;
+	}
+}
+
+HRESULT CDirectVobSubFilter::Transform(IMediaSample* pIn)
+{
+	HRESULT hr;
+
+
+	REFERENCE_TIME rtStart, rtStop;
+	if(SUCCEEDED(pIn->GetTime(&rtStart, &rtStop)))
+	{
+		double dRate = m_pInput->CurrentRate();
+		
+		m_tPrev = m_pInput->CurrentStartTime() + dRate*rtStart;
+
+		REFERENCE_TIME rtAvgTimePerFrame = rtStop - rtStart;
+		if(CComQIPtr<ISubClock2> pSC2 = m_pSubClock)
+		{
+			REFERENCE_TIME rt;
+			 if(S_OK == pSC2->GetAvgTimePerFrame(&rt))
+				 rtAvgTimePerFrame = rt;
+		}
+
+		m_fps = 10000000.0/rtAvgTimePerFrame / dRate;
+	}
+
+	//
+
+	{
+		CAutoLock cAutoLock(&m_csQueueLock);
+
+		if(m_pSubPicQueue)
+		{
+			m_pSubPicQueue->SetTime(CalcCurrentTime());
+			m_pSubPicQueue->SetFPS(m_fps);
+		}
+	}
+
+	//
+
+	BYTE* pDataIn = NULL;
+	if(FAILED(pIn->GetPointer(&pDataIn)) || !pDataIn)
+		return S_FALSE;
+
+	const CMediaType& mt = m_pInput->CurrentMediaType();
+
+	BITMAPINFOHEADER bihIn;
+	ExtractBIH(&mt, &bihIn);
+
+	bool fYV12 = (mt.subtype == MEDIASUBTYPE_YV12 || mt.subtype == MEDIASUBTYPE_I420 || mt.subtype == MEDIASUBTYPE_IYUV);
+	int bpp = fYV12 ? 8 : bihIn.biBitCount;
+	DWORD black = fYV12 ? 0x10101010 : (bihIn.biCompression == '2YUY') ? 0x80108010 : 0;
+
+	CSize sub(m_w, m_h);
+	CSize in(bihIn.biWidth, bihIn.biHeight);
+
+	if(FAILED(Copy((BYTE*)m_pTempPicBuff, pDataIn, sub, in, bpp, mt.subtype, black))) 
+		return E_FAIL;
+
+	if(fYV12)
+	{
+		BYTE* pSubV = (BYTE*)m_pTempPicBuff + (sub.cx*bpp>>3)*sub.cy;
+		BYTE* pInV = pDataIn + (in.cx*bpp>>3)*in.cy;
+		sub.cx >>= 1; sub.cy >>= 1; in.cx >>= 1; in.cy >>= 1;
+		BYTE* pSubU = pSubV + (sub.cx*bpp>>3)*sub.cy;
+		BYTE* pInU = pInV + (in.cx*bpp>>3)*in.cy;
+		if(FAILED(Copy(pSubV, pInV, sub, in, bpp, mt.subtype, 0x80808080)))
+			return E_FAIL;
+		if(FAILED(Copy(pSubU, pInU, sub, in, bpp, mt.subtype, 0x80808080)))
+			return E_FAIL;
+	}
+
+	//
+
+	SubPicDesc spd = m_spd;
+
+	CComPtr<IMediaSample> pOut;
+	BYTE* pDataOut = NULL;
+	if(FAILED(hr = GetDeliveryBuffer(spd.w, spd.h, &pOut))
+	|| FAILED(hr = pOut->GetPointer(&pDataOut)))
+		return hr;
+
+	pOut->SetTime(&rtStart, &rtStop);
+	pOut->SetMediaTime(NULL, NULL);
+
+	pOut->SetDiscontinuity(pIn->IsDiscontinuity() == S_OK);
+	pOut->SetSyncPoint(pIn->IsSyncPoint() == S_OK);
+	pOut->SetPreroll(pIn->IsPreroll() == S_OK);
+
+	// 
+
+	BITMAPINFOHEADER bihOut;
+	ExtractBIH(&m_pOutput->CurrentMediaType(), &bihOut);
+
+	bool fInputFlipped = bihIn.biHeight >= 0 && bihIn.biCompression <= 3;
+	bool fOutputFlipped = bihOut.biHeight >= 0 && bihOut.biCompression <= 3;
+
+	bool fFlip = fInputFlipped != fOutputFlipped;
+	if(m_fFlipPicture) fFlip = !fFlip;
+	if(m_fMSMpeg4Fix) fFlip = !fFlip;
+//	if(m_fDivxPlusFix) fFlip = !fFlip;
+
+	bool fFlipSub = fOutputFlipped;
+	if(m_fFlipSubtitles) fFlipSub = !fFlipSub;
+//	if(m_fDivxPlusFix) fFlipSub = !fFlipSub;
+
+	//
+
+	{
+		CAutoLock cAutoLock(&m_csQueueLock);
+
+		if(m_pSubPicQueue)
+		{
+			CComPtr<ISubPic> pSubPic;
+			if(SUCCEEDED(m_pSubPicQueue->LookupSubPic(CalcCurrentTime(), &pSubPic)) && pSubPic)
+			{
+				CRect r;
+				pSubPic->GetDirtyRect(r);
+
+				if(fFlip ^ fFlipSub)
+					spd.h = -spd.h;
+
+				pSubPic->AlphaBlt(r, r, &spd);
+			}
+		}
+	}
+
+	CopyBuffer(pDataOut, (BYTE*)spd.bits, spd.w, abs(spd.h)*(fFlip?-1:1), spd.pitch, mt.subtype);
+
+	PrintMessages(pDataOut);
+
+	return m_pOutput->Deliver(pOut);
+}
+
+// CBaseFilter
+
+CBasePin* CDirectVobSubFilter::GetPin(int n)
+{
+	if(n < __super::GetPinCount())
+		return __super::GetPin(n);
+
+	n -= __super::GetPinCount();
+
+	if(n >= 0 && n < m_pTextInput.GetCount())
+		return m_pTextInput[n]; 
+
+	n -= m_pTextInput.GetCount();
+
+	return NULL;
+}
+
+int CDirectVobSubFilter::GetPinCount()
+{
+	return __super::GetPinCount() + m_pTextInput.GetCount();
+}
+
+HRESULT CDirectVobSubFilter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
+{
+	if(pGraph)
+	{
+		AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+		if(!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 0))
+		{
+			unsigned __int64 ver = GetFileVersion(_T("divx_c32.ax"));
+			if(((ver >> 48)&0xffff) == 4 && ((ver >> 32)&0xffff) == 2)
+			{
+				DWORD dwVersion = GetVersion();
+				DWORD dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
+				DWORD dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
+				
+				if(dwVersion < 0x80000000 && dwWindowsMajorVersion >= 5)
+				{
+					AfxMessageBox(IDS_DIVX_WARNING);
+					theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 1);
+				}
+			}
+		}
+
+		/*removeme*/
+		if(!g_RegOK)
+		{
+			DllRegisterServer();
+			g_RegOK = true;
+		}
+	}
+	else
+	{
+		if(m_hSystrayThread)
+		{
+			SendMessage(m_tbid.hSystrayWnd, WM_CLOSE, 0, 0);
+
+			if(WaitForSingleObject(m_hSystrayThread, 10000) != WAIT_OBJECT_0)
+			{
+				DbgLog((LOG_TRACE, 0, _T("CALL THE AMBULANCE!!!")));
+				TerminateThread(m_hSystrayThread, (DWORD)-1);
+			}
+
+			m_hSystrayThread = 0;
+		}
+	}
+
+	return __super::JoinFilterGraph(pGraph, pName);
+}
+
+STDMETHODIMP CDirectVobSubFilter::QueryFilterInfo(FILTER_INFO* pInfo)
+{
+    CheckPointer(pInfo, E_POINTER);
+    ValidateReadWritePtr(pInfo, sizeof(FILTER_INFO));
+
+	if(!get_Forced())
+		return __super::QueryFilterInfo(pInfo);
+
+	wcscpy(pInfo->achName, L"DirectVobSub (forced auto-loading version)");
+	if(pInfo->pGraph = m_pGraph) m_pGraph->AddRef();
+	
+	return S_OK;
+}
+
+// CTransformFilter
+
+HRESULT CDirectVobSubFilter::SetMediaType(PIN_DIRECTION dir, const CMediaType* pmt)
+{
+	HRESULT hr = __super::SetMediaType(dir, pmt);
+	if(FAILED(hr)) return hr;
+
+	if(dir == PINDIR_INPUT)
+	{
+		CAutoLock cAutoLock(&m_csReceive);
+
+		REFERENCE_TIME atpf = 
+			pmt->formattype == FORMAT_VideoInfo ? ((VIDEOINFOHEADER*)pmt->Format())->AvgTimePerFrame :
+			pmt->formattype == FORMAT_VideoInfo2 ? ((VIDEOINFOHEADER2*)pmt->Format())->AvgTimePerFrame :
+			0;
+
+		m_fps = atpf ? 10000000.0 / atpf : 25;
+
+        InitSubPicQueue();
+	}
+	else if(dir == PINDIR_OUTPUT)
+	{
+
+	}
+
+	return hr;
+}
+
+HRESULT CDirectVobSubFilter::CheckConnect(PIN_DIRECTION dir, IPin* pPin)
+{
+	if(dir == PINDIR_INPUT)
+	{
+	}
+	else if(dir == PINDIR_OUTPUT)
+	{
+		/*removeme*/
+		if(HmGyanusVagyTeNekem(pPin)) return(E_FAIL);
+	}
+
+	return __super::CheckConnect(dir, pPin);
+}
+
+HRESULT CDirectVobSubFilter::CompleteConnect(PIN_DIRECTION dir, IPin* pReceivePin)
+{
+	if(dir == PINDIR_INPUT)
+	{
+		CComPtr<IBaseFilter> pFilter;
+
+		// needed when we have a decoder with a version number of 3.x
+		if(SUCCEEDED(m_pGraph->FindFilterByName(L"DivX MPEG-4 DVD Video Decompressor ", &pFilter))
+			&& (GetFileVersion(_T("divx_c32.ax")) >> 48) <= 4
+		|| SUCCEEDED(m_pGraph->FindFilterByName(L"Microcrap MPEG-4 Video Decompressor", &pFilter))
+		|| SUCCEEDED(m_pGraph->FindFilterByName(L"Microsoft MPEG-4 Video Decompressor", &pFilter)) 
+			&& (GetFileVersion(_T("mpg4ds32.ax")) >> 48) <= 3)
+		{
+			m_fMSMpeg4Fix = true;
+		}
+	}
+	else if(dir == PINDIR_OUTPUT)
+	{
+		if(!m_hSystrayThread)
+		{
+			m_tbid.graph = m_pGraph;
+			m_tbid.dvs = static_cast<IDirectVobSub*>(this);
+
+			DWORD tid;
+			m_hSystrayThread = CreateThread(0, 0, SystrayThreadProc, &m_tbid, 0, &tid);
+		}
+
+		// HACK: triggers CBaseVideoFilter::SetMediaType to adjust m_w/m_h/.. and InitSubPicQueue() to realloc buffers
+		m_pInput->SetMediaType(&m_pInput->CurrentMediaType());
+	}
+
+	return __super::CompleteConnect(dir, pReceivePin);
+}
+
+HRESULT CDirectVobSubFilter::BreakConnect(PIN_DIRECTION dir)
+{
+	if(dir == PINDIR_INPUT)
+	{
+		if(m_pOutput->IsConnected())
+		{
+			m_pOutput->GetConnected()->Disconnect();
+			m_pOutput->Disconnect();
+		}
+	}
+	else if(dir == PINDIR_OUTPUT)
+	{
+		// not really needed, but may free up a little memory
+		CAutoLock cAutoLock(&m_csQueueLock);
+		m_pSubPicQueue = NULL;
+	}
+
+	return __super::BreakConnect(dir);
+}
+
+HRESULT CDirectVobSubFilter::StartStreaming()
+{
+	m_fLoading = false;
+
+	InitSubPicQueue();
+
+	m_tbid.fRunOnce = true;
+
+	CComPtr<IBaseFilter> pFilter;
+	m_fDivxPlusFix = SUCCEEDED(m_pGraph->FindFilterByName(L"HPlus YUV Video Renderer", &pFilter));
+
+	put_MediaFPS(m_fMediaFPSEnabled, m_MediaFPS);
+
+	return __super::StartStreaming();
+}
+
+HRESULT CDirectVobSubFilter::StopStreaming()
+{
+	InvalidateSubtitle();
+
+	return __super::StopStreaming();
+}
+
+HRESULT CDirectVobSubFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
+{
+	m_tPrev = tStart;
+    return __super::NewSegment(tStart, tStop, dRate);
+}
+
+//
+
+REFERENCE_TIME CDirectVobSubFilter::CalcCurrentTime()
+{
+	REFERENCE_TIME rt = m_pSubClock ? m_pSubClock->GetTime() : m_tPrev;
+	return (rt - 10000i64*m_SubtitleDelay) * m_SubtitleSpeedMul / m_SubtitleSpeedDiv; // no, it won't overflow if we use normal parameters (__int64 is enough for about 2000 hours if we multiply it by the max: 65536 as m_SubtitleSpeedMul)
+}
+
+void CDirectVobSubFilter::InitSubPicQueue()
+{
+	CAutoLock cAutoLock(&m_csQueueLock);
+
+	m_pSubPicQueue = NULL;
+
+	m_pTempPicBuff.Free();
+	m_pTempPicBuff.Allocate(4*m_w*m_h);
+
+	const GUID& subtype = m_pInput->CurrentMediaType().subtype;
+
+	BITMAPINFOHEADER bihIn;
+	ExtractBIH(&m_pInput->CurrentMediaType(), &bihIn);
+
+	m_spd.type = -1;
+	if(subtype == MEDIASUBTYPE_YV12) m_spd.type = MSP_YV12;
+	else if(subtype == MEDIASUBTYPE_I420 || subtype == MEDIASUBTYPE_IYUV) m_spd.type = MSP_IYUV;
+	else if(subtype == MEDIASUBTYPE_YUY2) m_spd.type = MSP_YUY2;
+	else if(subtype == MEDIASUBTYPE_RGB32) m_spd.type = MSP_RGB32;
+	else if(subtype == MEDIASUBTYPE_RGB24) m_spd.type = MSP_RGB24;
+	else if(subtype == MEDIASUBTYPE_RGB565) m_spd.type = MSP_RGB16;
+	else if(subtype == MEDIASUBTYPE_RGB555) m_spd.type = MSP_RGB15;
+	m_spd.w = m_w;
+	m_spd.h = m_h;
+	m_spd.bpp = (m_spd.type == MSP_YV12 || m_spd.type == MSP_IYUV) ? 8 : bihIn.biBitCount;
+	m_spd.pitch = m_spd.w*m_spd.bpp>>3;
+	m_spd.bits = (void*)m_pTempPicBuff;
+
+	CComPtr<ISubPicAllocator> pSubPicAllocator = new CMemSubPicAllocator(m_spd.type, CSize(m_w, m_h));
+
+	CSize video(bihIn.biWidth, bihIn.biHeight), window = video;
+	if(AdjustFrameSize(window)) video += video;
+	ASSERT(window == CSize(m_w, m_h));
+
+	pSubPicAllocator->SetCurSize(window);
+	pSubPicAllocator->SetCurVidRect(CRect(CPoint((window.cx - video.cx)/2, (window.cy - video.cy)/2), video));
+
+	HRESULT hr = S_OK;
+	m_pSubPicQueue = m_fDoPreBuffering 
+		? (ISubPicQueue*)new CSubPicQueue(10, pSubPicAllocator, &hr)
+		: (ISubPicQueue*)new CSubPicQueueNoThread(pSubPicAllocator, &hr);
+
+	if(FAILED(hr)) m_pSubPicQueue = NULL;
+
+	UpdateSubtitle(false);
+
+	if(m_hbm) {DeleteObject(m_hbm); m_hbm = NULL;}
+	if(m_hdc) {DeleteDC(m_hdc); m_hdc = NULL;}
+
+	struct {BITMAPINFOHEADER bih; DWORD mask[3];} b = {{sizeof(BITMAPINFOHEADER), m_w, -(int)m_h, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0}, 0xFF0000, 0x00FF00, 0x0000FF};
+	m_hdc = CreateCompatibleDC(NULL);
+	m_hbm = CreateDIBSection(m_hdc, (BITMAPINFO*)&b, DIB_RGB_COLORS, NULL, NULL, 0);
+
+	BITMAP bm;
+	GetObject(m_hbm, sizeof(bm), &bm);
+	memsetd(bm.bmBits, 0xFF000000, bm.bmHeight*bm.bmWidthBytes);
+}
+
+bool CDirectVobSubFilter::AdjustFrameSize(CSize& s)
+{
+	int horizontal, vertical, resx2, resx2minw, resx2minh;
+	get_ExtendPicture(&horizontal, &vertical, &resx2, &resx2minw, &resx2minh);
+
+	bool fRet = (resx2 == 1) || (resx2 == 2 && s.cx*s.cy <= resx2minw*resx2minh);
+
+	if(fRet)
+	{
+		s.cx <<= 1; 
+		s.cy <<= 1;
+	}
+
+	int h;
+	switch(vertical&0x7f)
+	{
+	case 1:
+		h = s.cx * 9 / 16;
+		if(s.cy < h || !!(vertical&0x80)) s.cy = (h + 3) & ~3;
+		break;
+	case 2:
+		h = s.cx * 3 / 4;
+		if(s.cy < h || !!(vertical&0x80)) s.cy = (h + 3) & ~3;
+		break;
+	case 3:
+		h = 480;
+		if(s.cy < h || !!(vertical&0x80)) s.cy = (h + 3) & ~3;
+		break;
+	case 4:
+		h = 576;
+		if(s.cy < h || !!(vertical&0x80)) s.cy = (h + 3) & ~3;
+		break;
+	}
+
+	if(horizontal == 1)
+	{
+		s.cx = (s.cx + 31) & ~31;
+		s.cy = (s.cy + 1) & ~1;
+	}
+
+	return(fRet);
+}
+
+STDMETHODIMP CDirectVobSubFilter::Count(DWORD* pcStreams)
+{
+	if(!pcStreams) return E_POINTER;
+
+	*pcStreams = 0;
+
+	int nLangs = 0;
+	if(SUCCEEDED(get_LanguageCount(&nLangs)))
+		(*pcStreams) += nLangs;
+
+	(*pcStreams) += 2; // enable ... disable
+
+	(*pcStreams) += 2; // normal flipped
+
+	return S_OK;
+}
+
+#define MAXPREFLANGS 5
+
+int CDirectVobSubFilter::FindPreferedLanguage(bool fHideToo)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	int nLangs;
+	get_LanguageCount(&nLangs);
+
+	if(nLangs <= 0) return(0);
+
+	for(int i = 0; i < MAXPREFLANGS; i++)
+	{
+		CString tmp;
+		tmp.Format(IDS_RL_LANG, i);
+
+		CString lang = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp);
+		
+		if(!lang.IsEmpty())
+		{
+			for(int ret = 0; ret < nLangs; ret++)
+			{
+				CString l;
+				WCHAR* pName = NULL;
+				get_LanguageName(ret, &pName);
+				l = pName;
+				CoTaskMemFree(pName);
+
+				if(!l.CompareNoCase(lang)) return(ret);
+			}
+		}
+	}
+
+	return(0);
+}
+
+void CDirectVobSubFilter::UpdatePreferedLanguages(CString l)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CString langs[MAXPREFLANGS+1];
+
+	int i = 0, j = 0, k = -1;
+	for(; i < MAXPREFLANGS; i++)
+	{
+		CString tmp;
+		tmp.Format(IDS_RL_LANG, i);
+
+		langs[j] = theApp.GetProfileString(ResStr(IDS_R_PREFLANGS), tmp);
+
+		if(!langs[j].IsEmpty()) 
+		{
+			if(!langs[j].CompareNoCase(l)) k = j;
+			j++;
+		}
+	}
+
+	if(k == -1)
+	{
+		langs[k = j] = l;
+		j++;
+	}
+
+	// move the selected to the top of the list
+
+	while(k > 0)
+	{
+		CString tmp = langs[k]; langs[k] = langs[k-1]; langs[k-1] = tmp;
+		k--;
+	}
+
+	// move "Hide subtitles" to the last position if it wasn't our selection
+
+	CString hidesubs;
+	hidesubs.LoadString(IDS_M_HIDESUBTITLES);
+
+	for(k = 1; k < j; k++)
+	{
+		if(!langs[k].CompareNoCase(hidesubs)) break;
+	}
+
+	while(k < j-1)
+	{
+		CString tmp = langs[k]; langs[k] = langs[k+1]; langs[k+1] = tmp;
+		k++;
+	}
+
+	for(i = 0; i < j; i++)
+	{
+		CString tmp;
+		tmp.Format(IDS_RL_LANG, i);
+
+		theApp.WriteProfileString(ResStr(IDS_R_PREFLANGS), tmp, langs[i]);
+	}
+}
+
+STDMETHODIMP CDirectVobSubFilter::Enable(long lIndex, DWORD dwFlags)
+{
+	if(!(dwFlags & AMSTREAMSELECTENABLE_ENABLE))
+		return E_NOTIMPL;
+
+	int nLangs = 0;
+	get_LanguageCount(&nLangs);
+
+	if(!(lIndex >= 0 && lIndex < nLangs+2+2)) 
+		return E_INVALIDARG;
+
+	int i = lIndex-1;
+
+	if(i == -1 && !m_fLoading) // we need this because when loading something stupid media player pushes the first stream it founds, which is "enable" in our case
+	{
+		put_HideSubtitles(false);
+	}
+	else if(i >= 0 && i < nLangs)
+	{
+		put_HideSubtitles(false);
+		put_SelectedLanguage(i);
+
+		WCHAR* pName = NULL;
+		if(SUCCEEDED(get_LanguageName(i, &pName)))
+		{
+			UpdatePreferedLanguages(CString(pName));
+			if(pName) CoTaskMemFree(pName);
+		}
+	}
+	else if(i == nLangs && !m_fLoading)
+	{
+		put_HideSubtitles(true);
+	}
+	else if((i == nLangs+1 || i == nLangs+2) && !m_fLoading)
+	{
+		put_Flip(i == nLangs+2, m_fFlipSubtitles);
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSubFilter::Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	int nLangs = 0;
+	get_LanguageCount(&nLangs);
+
+	if(!(lIndex >= 0 && lIndex < nLangs+2+2)) 
+		return E_INVALIDARG;
+
+	int i = lIndex-1;
+
+	if(ppmt) *ppmt = CreateMediaType(&m_pInput->CurrentMediaType());
+
+	if(pdwFlags)
+	{
+		*pdwFlags = 0;
+
+		if(i == -1 && !m_fHideSubtitles
+		|| i >= 0 && i < nLangs && i == m_iSelectedLanguage
+		|| i == nLangs && m_fHideSubtitles
+		|| i == nLangs+1 && !m_fFlipPicture
+		|| i == nLangs+2 && m_fFlipPicture)
+		{
+			*pdwFlags |= AMSTREAMSELECTINFO_ENABLED;
+		}
+	}
+
+	if(plcid) *plcid = 0;
+
+	if(pdwGroup) *pdwGroup = 0x648E51;
+
+	if(ppszName)
+	{
+		*ppszName = NULL;
+
+		CStringW str;
+		if(i == -1) str = ResStr(IDS_M_SHOWSUBTITLES);
+		else if(i >= 0 && i < nLangs) get_LanguageName(i, ppszName);
+		else if(i == nLangs) str = ResStr(IDS_M_HIDESUBTITLES);
+		else if(i == nLangs+1) {str = ResStr(IDS_M_ORIGINALPICTURE); if(pdwGroup) (*pdwGroup)++;}
+		else if(i == nLangs+2) {str = ResStr(IDS_M_FLIPPEDPICTURE); if(pdwGroup) (*pdwGroup)++;}
+
+		if(!str.IsEmpty())
+		{
+			*ppszName = (WCHAR*)CoTaskMemAlloc((str.GetLength()+1)*sizeof(WCHAR));
+			if(*ppszName == NULL) return S_FALSE;
+			wcscpy(*ppszName, str);
+		}
+	}
+
+	if(ppObject) *ppObject = NULL;
+
+	if(ppUnk) *ppUnk = NULL;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDirectVobSubFilter::GetClassID(CLSID* pClsid)
+{
+    if(pClsid == NULL) return E_POINTER;
+	*pClsid = m_clsid;
+    return NOERROR;
+}
+
+STDMETHODIMP CDirectVobSubFilter::GetPages(CAUUID* pPages)
+{
+    CheckPointer(pPages, E_POINTER);
+
+	pPages->cElems = 7;
+    pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID)*pPages->cElems);
+
+	if(pPages->pElems == NULL) return E_OUTOFMEMORY;
+
+	int i = 0;
+    pPages->pElems[i++] = __uuidof(CDVSMainPPage);
+    pPages->pElems[i++] = __uuidof(CDVSGeneralPPage);
+    pPages->pElems[i++] = __uuidof(CDVSMiscPPage);
+    pPages->pElems[i++] = __uuidof(CDVSTimingPPage);
+    pPages->pElems[i++] = __uuidof(CDVSColorPPage);
+    pPages->pElems[i++] = __uuidof(CDVSPathsPPage);
+    pPages->pElems[i++] = __uuidof(CDVSAboutPPage);
+
+    return NOERROR;
+}
+
+// IDirectVobSub
+
+STDMETHODIMP CDirectVobSubFilter::put_FileName(WCHAR* fn)
+{
+	HRESULT hr = CDirectVobSub::put_FileName(fn);
+
+	if(hr == S_OK && !Open()) 
+	{
+		m_FileName.Empty();
+		hr = E_FAIL;
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::get_LanguageCount(int* nLangs)
+{
+	HRESULT hr = CDirectVobSub::get_LanguageCount(nLangs);
+
+	if(hr == NOERROR && nLangs)
+	{
+        CAutoLock cAutolock(&m_csQueueLock);
+
+		*nLangs = 0;
+		POSITION pos = m_pSubStreams.GetHeadPosition();
+		while(pos) (*nLangs) += m_pSubStreams.GetNext(pos)->GetStreamCount();
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::get_LanguageName(int iLanguage, WCHAR** ppName)
+{
+	HRESULT hr = CDirectVobSub::get_LanguageName(iLanguage, ppName);
+
+	if(!ppName) return E_POINTER;
+
+	if(hr == NOERROR)
+	{
+        CAutoLock cAutolock(&m_csQueueLock);
+
+		hr = E_INVALIDARG;
+
+		int i = iLanguage;
+
+		POSITION pos = m_pSubStreams.GetHeadPosition();
+		while(i >= 0 && pos)
+		{
+			CComPtr<ISubStream> pSubStream = m_pSubStreams.GetNext(pos);
+
+			if(i < pSubStream->GetStreamCount())
+			{
+				pSubStream->GetStreamInfo(i, ppName, NULL);
+				hr = NOERROR;
+				break;
+			}
+
+			i -= pSubStream->GetStreamCount();
+		}
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_SelectedLanguage(int iSelected)
+{
+	HRESULT hr = CDirectVobSub::put_SelectedLanguage(iSelected);
+
+	if(hr == NOERROR)
+	{
+		UpdateSubtitle(false);
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_HideSubtitles(bool fHideSubtitles)
+{
+	HRESULT hr = CDirectVobSub::put_HideSubtitles(fHideSubtitles);
+
+	if(hr == NOERROR)
+	{
+		UpdateSubtitle(false);
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_PreBuffering(bool fDoPreBuffering)
+{
+	HRESULT hr = CDirectVobSub::put_PreBuffering(fDoPreBuffering);
+
+	if(hr == NOERROR)
+	{
+		InitSubPicQueue();
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_Placement(bool fOverridePlacement, int xperc, int yperc)
+{
+	HRESULT hr = CDirectVobSub::put_Placement(fOverridePlacement, xperc, yperc);
+
+	if(hr == NOERROR)
+	{
+		UpdateSubtitle(true);
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fReserved)
+{
+	HRESULT hr = CDirectVobSub::put_VobSubSettings(fBuffer, fOnlyShowForcedSubs, fReserved);
+
+	if(hr == NOERROR)
+	{
+//		UpdateSubtitle(false);
+		InvalidateSubtitle();
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer)
+{
+	HRESULT hr = CDirectVobSub::put_TextSettings(lf, lflen, color, fShadow, fOutline, fAdvancedRenderer);
+	
+	if(hr == NOERROR)
+	{
+//		UpdateSubtitle(true);
+		InvalidateSubtitle();
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_SubtitleTiming(int delay, int speedmul, int speeddiv)
+{
+	HRESULT hr = CDirectVobSub::put_SubtitleTiming(delay, speedmul, speeddiv);
+
+	if(hr == NOERROR)
+	{
+		InvalidateSubtitle();
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::get_MediaFPS(bool* fEnabled, double* fps)
+{
+	HRESULT hr = CDirectVobSub::get_MediaFPS(fEnabled, fps);
+
+	CComQIPtr<IMediaSeeking> pMS = m_pGraph;
+	double rate;
+	if(pMS && SUCCEEDED(pMS->GetRate(&rate)))
+	{
+		m_MediaFPS = rate * m_fps;
+		if(fps) *fps = m_MediaFPS;
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_MediaFPS(bool fEnabled, double fps)
+{
+	HRESULT hr = CDirectVobSub::put_MediaFPS(fEnabled, fps);
+
+	CComQIPtr<IMediaSeeking> pMS = m_pGraph;
+	if(pMS)
+	{
+		if(hr == NOERROR)
+		{
+			hr = pMS->SetRate(m_fMediaFPSEnabled ? m_MediaFPS / m_fps : 1.0);
+		}
+
+		double dRate;
+		if(SUCCEEDED(pMS->GetRate(&dRate)))
+			m_MediaFPS = dRate * m_fps;
+	}
+
+	return hr;
+}
+
+STDMETHODIMP CDirectVobSubFilter::get_ZoomRect(NORMALIZEDRECT* rect)
+{
+	return E_NOTIMPL;
+}
+
+STDMETHODIMP CDirectVobSubFilter::put_ZoomRect(NORMALIZEDRECT* rect)
+{
+	return E_NOTIMPL;
+}
+
+// IDirectVobSub2
+
+STDMETHODIMP CDirectVobSubFilter::put_TextSettings(STSStyle* pDefStyle)
+{
+	HRESULT hr = CDirectVobSub::put_TextSettings(pDefStyle);
+	
+	if(hr == NOERROR)
+	{
+		UpdateSubtitle(true);
+	}
+
+	return hr;
+}
+
+// IDirectVobSubFilterColor
+
+STDMETHODIMP CDirectVobSubFilter::HasConfigDialog(int iSelected)
+{
+	int nLangs;
+	if(FAILED(get_LanguageCount(&nLangs))) return E_FAIL;
+	return E_FAIL;
+	// TODO: temporally disabled since we don't have a new textsub/vobsub editor dlg for dvs yet
+//	return(nLangs >= 0 && iSelected < nLangs ? S_OK : E_FAIL);
+}
+
+STDMETHODIMP CDirectVobSubFilter::ShowConfigDialog(int iSelected, HWND hWndParent)
+{
+	// TODO: temporally disabled since we don't have a new textsub/vobsub editor dlg for dvs yet
+	return(E_FAIL);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+CDirectVobSubFilter2::CDirectVobSubFilter2(LPUNKNOWN punk, HRESULT* phr, const GUID& clsid) :
+	CDirectVobSubFilter(punk, phr, clsid)
+{
+}
+
+HRESULT CDirectVobSubFilter2::CheckConnect(PIN_DIRECTION dir, IPin* pPin)
+{
+	CPinInfo pi;
+	if(FAILED(pPin->QueryPinInfo(&pi))) return E_FAIL;
+
+	if(CComQIPtr<IDirectVobSub>(pi.pFilter)) return E_FAIL;
+
+	if(dir == PINDIR_INPUT)
+	{
+		CFilterInfo fi;
+		if(SUCCEEDED(pi.pFilter->QueryFilterInfo(&fi))
+		&& !wcsnicmp(fi.achName, L"Overlay Mixer", 13))
+			return(E_FAIL);
+	}
+	else
+	{
+	}
+
+	return __super::CheckConnect(dir, pPin);
+}
+
+HRESULT CDirectVobSubFilter2::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
+{
+	if(pGraph)
+	{
+		BeginEnumFilters(pGraph, pEF, pBF)
+		{
+			if(pBF != (IBaseFilter*)this && CComQIPtr<IDirectVobSub>(pBF))
+				return E_FAIL;
+		}
+		EndEnumFilters
+
+		// don't look... we will do some serious graph hacking again...
+		//
+		// we will add dvs2 to the filter graph cache
+		// - if the main app has already added some kind of renderer or overlay mixer (anything which accepts video on its input)
+		// and 
+		// - if we have a reason to auto-load (we don't want to make any trouble when there is no need :)
+		//
+		// This whole workaround is needed because the video stream will always be connected 
+		// to the pre-added filters first, no matter how high merit we have.
+
+		if(!get_Forced())
+		{
+			BeginEnumFilters(pGraph, pEF, pBF)
+			{
+				if(CComQIPtr<IDirectVobSub>(pBF))
+					continue;
+
+				CComPtr<IPin> pInPin = GetFirstPin(pBF, PINDIR_INPUT);
+				CComPtr<IPin> pOutPin = GetFirstPin(pBF, PINDIR_OUTPUT);
+
+				if(!pInPin)
+					continue;
+
+				CComPtr<IPin> pPin;
+				if(pInPin && SUCCEEDED(pInPin->ConnectedTo(&pPin))
+				|| pOutPin && SUCCEEDED(pOutPin->ConnectedTo(&pPin)))
+					continue;
+
+				if(pOutPin && GetFilterName(pBF) == _T("Overlay Mixer")) 
+					continue;
+
+				bool fVideoInputPin = false;
+
+				do
+				{
+					BITMAPINFOHEADER bih = {sizeof(BITMAPINFOHEADER), 384, 288, 1, 16, '2YUY', 384*288*2, 0, 0, 0, 0};
+
+					CMediaType cmt;
+					cmt.majortype = MEDIATYPE_Video;
+					cmt.subtype = MEDIASUBTYPE_YUY2;
+					cmt.formattype = FORMAT_VideoInfo;
+					cmt.pUnk = NULL;
+					cmt.bFixedSizeSamples = TRUE;
+					cmt.bTemporalCompression = TRUE;
+					cmt.lSampleSize = 384*288*2;
+					VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
+					memset(vih, 0, sizeof(VIDEOINFOHEADER));
+					memcpy(&vih->bmiHeader, &bih, sizeof(bih));
+					vih->AvgTimePerFrame = 400000;
+
+					if(SUCCEEDED(pInPin->QueryAccept(&cmt))) 
+					{
+						fVideoInputPin = true;
+						break;
+					}
+
+					VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)cmt.AllocFormatBuffer(sizeof(VIDEOINFOHEADER2));
+					memset(vih2, 0, sizeof(VIDEOINFOHEADER2));
+					memcpy(&vih2->bmiHeader, &bih, sizeof(bih));
+					vih2->AvgTimePerFrame = 400000;
+					vih2->dwPictAspectRatioX = 384;
+					vih2->dwPictAspectRatioY = 288;
+
+					if(SUCCEEDED(pInPin->QueryAccept(&cmt)))
+					{
+						fVideoInputPin = true;
+						break;
+					}
+				}
+				while(false);
+
+				if(fVideoInputPin)
+				{
+					CComPtr<IBaseFilter> pDVS;
+					if(ShouldWeAutoload(pGraph) && SUCCEEDED(pDVS.CoCreateInstance(__uuidof(CDirectVobSubFilter2))))
+					{
+						CComQIPtr<IDirectVobSub2>(pDVS)->put_Forced(true);
+						CComQIPtr<IGraphConfig>(pGraph)->AddFilterToCache(pDVS);
+					}
+
+					break;
+				}
+			}
+			EndEnumFilters
+		}
+	}
+	else
+	{
+	}
+
+	return __super::JoinFilterGraph(pGraph, pName);
+}
+
+HRESULT CDirectVobSubFilter2::CheckInputType(const CMediaType* mtIn)
+{
+    HRESULT hr = __super::CheckInputType(mtIn);
+
+	if(FAILED(hr) || m_pInput->IsConnected()) return hr;
+
+	if(!ShouldWeAutoload(m_pGraph)) return VFW_E_TYPE_NOT_ACCEPTED;
+
+	GetRidOfInternalScriptRenderer();
+
+	return NOERROR;
+}
+
+bool CDirectVobSubFilter2::ShouldWeAutoload(IFilterGraph* pGraph)
+{
+	TCHAR blacklistedapps[][32] = 
+	{
+		_T("WM8EUTIL."), // wmp8 encoder's dummy renderer releases the outputted media sample after calling Receive on its input pin (yes, even when dvobsub isn't registered at all)
+		_T("explorer."), // as some users reported thumbnail preview loads dvobsub, I've never experienced this yet...
+		_T("producer."), // this is real's producer
+	};
+
+	for(int i = 0; i < countof(blacklistedapps); i++)
+	{
+		if(theApp.m_AppName.Find(blacklistedapps[i]) >= 0) 
+			return(false);
+	}
+
+	int level;
+	bool m_fExternalLoad, m_fWebLoad, m_fEmbeddedLoad;
+	get_LoadSettings(&level, &m_fExternalLoad, &m_fWebLoad, &m_fEmbeddedLoad);
+
+	if(level < 0 || level >= 2) return(false);
+
+	bool fRet = false;
+
+	if(level == 1)
+		fRet = m_fExternalLoad = m_fWebLoad = m_fEmbeddedLoad = true;
+
+	// find text stream on known splitters
+
+	if(!fRet && m_fEmbeddedLoad)
+	{
+		CComPtr<IBaseFilter> pBF;
+		if((pBF = FindFilter(CLSID_OggSplitter, pGraph)) || (pBF = FindFilter(CLSID_AviSplitter, pGraph))
+		|| (pBF = FindFilter(L"{34293064-02F2-41D5-9D75-CC5967ACA1AB}", pGraph)) // matroska demux
+		|| (pBF = FindFilter(L"{0A68C3B5-9164-4a54-AFAF-995B2FF0E0D4}", pGraph)) // matroska source
+		|| (pBF = FindFilter(L"{149D2E01-C32E-4939-80F6-C07B81015A7A}", pGraph)) // matroska splitter
+		|| (pBF = FindFilter(L"{9AB95E90-1F37-427e-9B3D-257FB0CB25F7}", pGraph)) // Haali's matroska splitter (?)
+		|| (pBF = FindFilter(L"{55DA30FC-F16B-49fc-BAA5-AE59FC65F82D}", pGraph)) // Haali's matroska splitter			
+		|| (pBF = FindFilter(L"{52B63861-DC93-11CE-A099-00AA00479A58}", pGraph)) // 3ivx splitter
+		|| (pBF = FindFilter(L"{6D3688CE-3E9D-42F4-92CA-8A11119D25CD}", pGraph)) // our ogg source
+		|| (pBF = FindFilter(L"{9FF48807-E133-40AA-826F-9B2959E5232D}", pGraph)) // our ogg splitter
+		|| (pBF = FindFilter(L"{803E8280-F3CE-4201-982C-8CD8FB512004}", pGraph)) // dsm source
+		|| (pBF = FindFilter(L"{0912B4DD-A30A-4568-B590-7179EBB420EC}", pGraph)) // dsm splitter
+		|| (pBF = FindFilter(L"{3CCC052E-BDEE-408a-BEA7-90914EF2964B}", pGraph)) // mp4 source
+		|| (pBF = FindFilter(L"{61F47056-E400-43d3-AF1E-AB7DFFD4C4AD}", pGraph))) // mp4 splitter
+		{
+			BeginEnumPins(pBF, pEP, pPin)
+			{
+				BeginEnumMediaTypes(pPin, pEM, pmt)
+				{
+					if(pmt->majortype == MEDIATYPE_Text || pmt->majortype == MEDIATYPE_Subtitle)
+					{
+						fRet = true;
+						break;
+					}
+				}
+				EndEnumMediaTypes(pmt)
+				if(fRet) break;
+			}
+			EndEnumFilters
+		}
+	}
+
+	// find file name
+
+	CStringW fn;
+
+	BeginEnumFilters(pGraph, pEF, pBF)
+	{
+		if(CComQIPtr<IFileSourceFilter> pFSF = pBF)
+		{
+			LPOLESTR fnw = NULL;
+			if(!pFSF || FAILED(pFSF->GetCurFile(&fnw, NULL)) || !fnw)
+				continue;
+			fn = CString(fnw);
+			CoTaskMemFree(fnw);
+			break;
+		}
+	}
+	EndEnumFilters
+
+	if((m_fExternalLoad || m_fWebLoad) && (m_fWebLoad || !(wcsstr(fn, L"http://") || wcsstr(fn, L"mms://"))))
+	{
+		bool fTemp = m_fHideSubtitles;
+		fRet = !fn.IsEmpty() && SUCCEEDED(put_FileName((LPWSTR)(LPCWSTR)fn))
+			|| SUCCEEDED(put_FileName(L"c:\\tmp.srt")) 
+			|| fRet;
+		if(fTemp) m_fHideSubtitles = true;
+	}
+
+	return(fRet);
+}
+
+void CDirectVobSubFilter2::GetRidOfInternalScriptRenderer()
+{
+	while(CComPtr<IBaseFilter> pBF = FindFilter(L"{48025243-2D39-11CE-875D-00608CB78066}", m_pGraph))
+	{
+		BeginEnumPins(pBF, pEP, pPin)
+		{
+			PIN_DIRECTION dir;
+			CComPtr<IPin> pPinTo;
+
+			if(SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT 
+			&& SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
+			{
+				m_pGraph->Disconnect(pPinTo);
+				m_pGraph->Disconnect(pPin);
+				m_pGraph->ConnectDirect(pPinTo, GetPin(2 + m_pTextInput.GetCount()-1), NULL);
+			}
+		}
+		EndEnumPins
+
+		if(FAILED(m_pGraph->RemoveFilter(pBF)))
+			break;
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool CDirectVobSubFilter::Open()
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CAutoLock cAutolock(&m_csQueueLock);
+
+	m_pSubStreams.RemoveAll();
+
+	m_frd.files.RemoveAll();
+
+	CAtlArray<CString> paths;
+
+	for(int i = 0; i < 10; i++)
+	{
+		CString tmp;
+		tmp.Format(IDS_RP_PATH, i);
+		CString path = theApp.GetProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp);
+		if(!path.IsEmpty()) paths.Add(path);
+	}
+
+	CAtlArray<SubFile> ret;
+	GetSubFileNames(m_FileName, paths, ret);
+
+	for(int i = 0; i < ret.GetCount(); i++)
+	{
+		if(m_frd.files.Find(ret[i].fn))
+			continue;
+
+		CComPtr<ISubStream> pSubStream;
+
+		if(!pSubStream)
+		{
+			CAutoPtr<CVobSubFile> pVSF(new CVobSubFile(&m_csSubLock));
+			if(pVSF && pVSF->Open(ret[i].fn) && pVSF->GetStreamCount() > 0)
+			{
+				pSubStream = pVSF.Detach();
+				m_frd.files.AddTail(ret[i].fn.Left(ret[i].fn.GetLength()-4) + _T(".sub"));
+			}
+		}
+
+		if(!pSubStream)
+		{
+			CAutoPtr<ssf::CRenderer> pSSF(new ssf::CRenderer(&m_csSubLock));
+			if(pSSF && pSSF->Open(ret[i].fn) && pSSF->GetStreamCount() > 0)
+			{
+				pSubStream = pSSF.Detach();
+			}
+		}
+	    
+		if(!pSubStream)
+		{
+			CAutoPtr<CRenderedTextSubtitle> pRTS(new CRenderedTextSubtitle(&m_csSubLock));
+			if(pRTS && pRTS->Open(ret[i].fn, DEFAULT_CHARSET) && pRTS->GetStreamCount() > 0)
+			{
+				pSubStream = pRTS.Detach();
+				m_frd.files.AddTail(ret[i].fn + _T(".style"));
+			}
+		}
+	    
+		if(pSubStream)
+		{
+			m_pSubStreams.AddTail(pSubStream);
+			m_frd.files.AddTail(ret[i].fn);
+		}
+	}
+
+	for(int i = 0; i < m_pTextInput.GetCount(); i++)
+	{
+		if(m_pTextInput[i]->IsConnected())
+			m_pSubStreams.AddTail(m_pTextInput[i]->GetSubStream());
+	}
+
+	if(S_FALSE == put_SelectedLanguage(FindPreferedLanguage()))
+        UpdateSubtitle(false); // make sure pSubPicProvider of our queue gets updated even if the stream number hasn't changed
+
+	m_frd.RefreshEvent.Set();
+
+	return(m_pSubStreams.GetCount() > 0);
+}
+
+void CDirectVobSubFilter::UpdateSubtitle(bool fApplyDefStyle)
+{
+	CAutoLock cAutolock(&m_csQueueLock);
+
+	if(!m_pSubPicQueue) return;
+
+	InvalidateSubtitle();
+
+	CComPtr<ISubStream> pSubStream;
+
+	if(!m_fHideSubtitles)
+	{
+		int i = m_iSelectedLanguage;
+
+		for(POSITION pos = m_pSubStreams.GetHeadPosition(); i >= 0 && pos; pSubStream = NULL)
+		{
+			pSubStream = m_pSubStreams.GetNext(pos);
+
+			if(i < pSubStream->GetStreamCount()) 
+			{
+				CAutoLock cAutoLock(&m_csSubLock);
+				pSubStream->SetStream(i);
+				break;
+			}
+
+			i -= pSubStream->GetStreamCount();
+		}
+	}
+
+	SetSubtitle(pSubStream, fApplyDefStyle);
+}
+
+void CDirectVobSubFilter::SetSubtitle(ISubStream* pSubStream, bool fApplyDefStyle)
+{
+    CAutoLock cAutolock(&m_csQueueLock);
+
+	if(pSubStream)
+	{
+		CAutoLock cAutolock(&m_csSubLock);
+
+		CLSID clsid;
+		pSubStream->GetClassID(&clsid);
+
+		if(clsid == __uuidof(CVobSubFile))
+		{
+			CVobSubSettings* pVSS = (CVobSubFile*)(ISubStream*)pSubStream;
+
+			if(fApplyDefStyle)
+			{
+				pVSS->SetAlignment(m_fOverridePlacement, m_PlacementXperc, m_PlacementYperc, 1, 1);
+				pVSS->m_fOnlyShowForcedSubs = m_fOnlyShowForcedVobSubs;
+			}
+		}
+		else if(clsid == __uuidof(CVobSubStream))
+		{
+			CVobSubSettings* pVSS = (CVobSubStream*)(ISubStream*)pSubStream;
+
+			if(fApplyDefStyle)
+			{
+				pVSS->SetAlignment(m_fOverridePlacement, m_PlacementXperc, m_PlacementYperc, 1, 1);
+				pVSS->m_fOnlyShowForcedSubs = m_fOnlyShowForcedVobSubs;
+			}
+		}
+		else if(clsid == __uuidof(CRenderedTextSubtitle))
+		{
+			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)pSubStream;
+
+			if(fApplyDefStyle || pRTS->m_fUsingAutoGeneratedDefaultStyle)
+			{
+				STSStyle s = m_defStyle;
+
+				if(m_fOverridePlacement)
+				{
+					s.scrAlignment = 2;
+					int w = pRTS->m_dstScreenSize.cx;
+					int h = pRTS->m_dstScreenSize.cy;
+					int mw = w - s.marginRect.left - s.marginRect.right;
+					s.marginRect.bottom = h - MulDiv(h, m_PlacementYperc, 100);
+					s.marginRect.left = MulDiv(w, m_PlacementXperc, 100) - mw/2;
+					s.marginRect.right = w - (s.marginRect.left + mw);
+				}
+
+				pRTS->SetDefaultStyle(s);
+			}
+
+			pRTS->Deinit();
+		}
+	}
+
+	if(!fApplyDefStyle)
+	{
+		int i = 0;
+
+		POSITION pos = m_pSubStreams.GetHeadPosition();
+		while(pos)
+		{
+			CComPtr<ISubStream> pSubStream2 = m_pSubStreams.GetNext(pos);
+
+			if(pSubStream == pSubStream2)
+			{
+				m_iSelectedLanguage = i + pSubStream2->GetStream();
+				break;
+			}
+
+			i += pSubStream2->GetStreamCount();
+		}
+	}
+
+	m_nSubtitleId = (DWORD_PTR)pSubStream;
+
+	if(m_pSubPicQueue)
+		m_pSubPicQueue->SetSubPicProvider(CComQIPtr<ISubPicProvider>(pSubStream));
+}
+
+void CDirectVobSubFilter::InvalidateSubtitle(REFERENCE_TIME rtInvalidate, DWORD_PTR nSubtitleId)
+{
+    CAutoLock cAutolock(&m_csQueueLock);
+
+	if(m_pSubPicQueue)
+	{
+		if(nSubtitleId == -1 || nSubtitleId == m_nSubtitleId)
+			m_pSubPicQueue->Invalidate(rtInvalidate);
+	}
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void CDirectVobSubFilter::AddSubStream(ISubStream* pSubStream)
+{
+	CAutoLock cAutoLock(&m_csQueueLock);
+
+	POSITION pos = m_pSubStreams.Find(pSubStream);
+	if(!pos) m_pSubStreams.AddTail(pSubStream);
+
+	int len = m_pTextInput.GetCount();
+	for(int i = 0; i < m_pTextInput.GetCount(); i++)
+		if(m_pTextInput[i]->IsConnected()) len--;
+
+	if(len == 0)
+	{
+		HRESULT hr = S_OK;
+		m_pTextInput.Add(new CTextInputPin(this, m_pLock, &m_csSubLock, &hr));
+	}
+}
+
+void CDirectVobSubFilter::RemoveSubStream(ISubStream* pSubStream)
+{
+	CAutoLock cAutoLock(&m_csQueueLock);
+
+	POSITION pos = m_pSubStreams.Find(pSubStream);
+	if(pos) m_pSubStreams.RemoveAt(pos);
+}
+
+void CDirectVobSubFilter::Post_EC_OLE_EVENT(CString str, DWORD_PTR nSubtitleId)
+{
+	if(nSubtitleId != -1 && nSubtitleId != m_nSubtitleId)
+		return;
+
+	CComQIPtr<IMediaEventSink> pMES = m_pGraph;
+	if(!pMES) return;
+
+	CComBSTR bstr1("Text"), bstr2(" ");
+
+	str.Trim();
+	if(!str.IsEmpty()) bstr2 = CStringA(str);
+
+	pMES->Notify(EC_OLE_EVENT, (LONG_PTR)bstr1.Detach(), (LONG_PTR)bstr2.Detach());
+}
+
+////////////////////////////////////////////////////////////////
+
+void CDirectVobSubFilter::SetupFRD(CStringArray& paths, CAtlArray<HANDLE>& handles)
+{
+    CAutoLock cAutolock(&m_csSubLock);
+
+	for(int i = 2; i < handles.GetCount(); i++)
+	{
+		FindCloseChangeNotification(handles[i]);
+	}
+
+	paths.RemoveAll();
+	handles.RemoveAll();
+
+	handles.Add(m_frd.EndThreadEvent);
+	handles.Add(m_frd.RefreshEvent);
+
+	m_frd.mtime.SetCount(m_frd.files.GetCount());
+
+	POSITION pos = m_frd.files.GetHeadPosition();
+	for(int i = 0; pos; i++)
+	{
+		CString fn = m_frd.files.GetNext(pos);
+
+		CFileStatus status;
+		if(CFileGetStatus(fn, status)) 
+			m_frd.mtime[i] = status.m_mtime;
+
+		fn.Replace('\\', '/');
+		fn = fn.Left(fn.ReverseFind('/')+1);
+
+		bool fFound = false;
+
+		for(int j = 0; !fFound && j < paths.GetCount(); j++)
+		{
+			if(paths[j] == fn) fFound = true;
+		}
+
+		if(!fFound)
+		{
+			paths.Add(fn);
+
+			HANDLE h = FindFirstChangeNotification(fn, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); 
+			if(h != INVALID_HANDLE_VALUE) handles.Add(h);
+		}
+	}
+}
+
+DWORD CDirectVobSubFilter::ThreadProc()
+{	
+	SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST/*THREAD_PRIORITY_BELOW_NORMAL*/);
+
+	CStringArray paths;
+	CAtlArray<HANDLE> handles;
+
+	SetupFRD(paths, handles);
+
+	while(1)
+	{ 
+		DWORD idx = WaitForMultipleObjects(handles.GetCount(), handles.GetData(), FALSE, INFINITE);
+
+		if(idx == (WAIT_OBJECT_0 + 0)) // m_frd.hEndThreadEvent
+		{
+			break;
+		}
+		if(idx == (WAIT_OBJECT_0 + 1)) // m_frd.hRefreshEvent
+		{
+			SetupFRD(paths, handles);
+		}
+		else if(idx >= (WAIT_OBJECT_0 + 2) && idx < (WAIT_OBJECT_0 + handles.GetCount()))
+		{
+			bool fLocked = true;
+			IsSubtitleReloaderLocked(&fLocked);
+			if(fLocked) continue;
+
+			if(FindNextChangeNotification(handles[idx - WAIT_OBJECT_0]) == FALSE) 
+				break;
+
+			int j = 0;
+
+			POSITION pos = m_frd.files.GetHeadPosition();
+			for(int i = 0; pos && j == 0; i++)
+			{
+				CString fn = m_frd.files.GetNext(pos);
+
+				CFileStatus status;
+				if(CFileGetStatus(fn, status) && m_frd.mtime[i] != status.m_mtime) 
+				{
+					for(j = 0; j < 10; j++)
+					{
+						if(FILE* f = _tfopen(fn, _T("rb+"))) 
+						{
+							fclose(f);
+							j = 0;
+							break;
+						}
+						else
+						{
+							Sleep(100);
+							j++;
+						}
+					}
+				}
+			}
+
+			if(j > 0)
+			{
+				SetupFRD(paths, handles);
+			}
+			else
+			{
+				Sleep(500);
+
+				POSITION pos = m_frd.files.GetHeadPosition();
+				for(int i = 0; pos; i++)
+				{
+					CFileStatus status;
+					if(CFileGetStatus(m_frd.files.GetNext(pos), status) 
+						&& m_frd.mtime[i] != status.m_mtime) 
+					{
+						Open();
+						SetupFRD(paths, handles);
+						break;
+					}
+				}
+			}
+		}
+		else 
+		{
+			break;
+		}
+	}
+
+	for(int i = 2; i < handles.GetCount(); i++)
+	{
+		FindCloseChangeNotification(handles[i]);
+	}
+
+	return 0;
+}
diff --git a/vsfilter/DirectVobSubFilter.h b/vsfilter/DirectVobSubFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..3d2edc558a4f26a6af6ba8145cab040479f30d47
--- /dev/null
+++ b/vsfilter/DirectVobSubFilter.h
@@ -0,0 +1,196 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlsync.h>
+#include "DirectVobSub.h"
+#include "BaseVideoFilter/BaseVideoFilter.h"
+#include "subtitles/VobSubFile.h"
+#include "subtitles/RTS.h"
+#include "subtitles/SSF.h"
+
+typedef struct
+{
+	HWND hSystrayWnd;
+	IFilterGraph* graph;
+	IDirectVobSub* dvs;
+	bool fRunOnce, fShowIcon;
+} SystrayIconData;
+
+/* This is for graphedit */
+
+[uuid("93A22E7A-5091-45ef-BA61-6DA26156A5D0")]
+class CDirectVobSubFilter
+	: public CBaseVideoFilter
+	, public CDirectVobSub
+	, public ISpecifyPropertyPages
+	, public IAMStreamSelect
+	, public CAMThread
+{
+    friend class CTextInputPin;
+
+	CCritSec m_csQueueLock;
+	CComPtr<ISubPicQueue> m_pSubPicQueue;
+	void InitSubPicQueue();
+	SubPicDesc m_spd;
+
+	bool AdjustFrameSize(CSize& s);
+
+protected:
+	void GetOutputSize(int& w, int& h, int& arx, int& ary);
+	HRESULT Transform(IMediaSample* pIn);
+
+public:
+    CDirectVobSubFilter(LPUNKNOWN punk, HRESULT* phr, const GUID& clsid = __uuidof(CDirectVobSubFilter));
+	virtual ~CDirectVobSubFilter();
+
+    DECLARE_IUNKNOWN;
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+    // CBaseFilter
+
+	CBasePin* GetPin(int n);
+	int GetPinCount();
+
+	STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName);
+	STDMETHODIMP QueryFilterInfo(FILTER_INFO* pInfo);
+
+    // CTransformFilter
+	HRESULT SetMediaType(PIN_DIRECTION dir, const CMediaType* pMediaType),
+			CheckConnect(PIN_DIRECTION dir, IPin* pPin),
+			CompleteConnect(PIN_DIRECTION dir, IPin* pReceivePin),
+			BreakConnect(PIN_DIRECTION dir),
+			StartStreaming(), 
+			StopStreaming(),
+			NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
+
+    CAtlArray<CTextInputPin*> m_pTextInput;
+
+    // IDirectVobSub
+    STDMETHODIMP put_FileName(WCHAR* fn);
+	STDMETHODIMP get_LanguageCount(int* nLangs);
+	STDMETHODIMP get_LanguageName(int iLanguage, WCHAR** ppName);
+	STDMETHODIMP put_SelectedLanguage(int iSelected);
+    STDMETHODIMP put_HideSubtitles(bool fHideSubtitles);
+	STDMETHODIMP put_PreBuffering(bool fDoPreBuffering);
+    STDMETHODIMP put_Placement(bool fOverridePlacement, int xperc, int yperc);
+    STDMETHODIMP put_VobSubSettings(bool fBuffer, bool fOnlyShowForcedSubs, bool fPolygonize);
+    STDMETHODIMP put_TextSettings(void* lf, int lflen, COLORREF color, bool fShadow, bool fOutline, bool fAdvancedRenderer);
+    STDMETHODIMP put_SubtitleTiming(int delay, int speedmul, int speeddiv);
+    STDMETHODIMP get_MediaFPS(bool* fEnabled, double* fps);
+    STDMETHODIMP put_MediaFPS(bool fEnabled, double fps);
+    STDMETHODIMP get_ZoomRect(NORMALIZEDRECT* rect);
+    STDMETHODIMP put_ZoomRect(NORMALIZEDRECT* rect);
+	STDMETHODIMP HasConfigDialog(int iSelected);
+	STDMETHODIMP ShowConfigDialog(int iSelected, HWND hWndParent);
+
+	// IDirectVobSub2
+	STDMETHODIMP put_TextSettings(STSStyle* pDefStyle);
+
+    // ISpecifyPropertyPages
+    STDMETHODIMP GetPages(CAUUID* pPages);
+
+	// IAMStreamSelect
+	STDMETHODIMP Count(DWORD* pcStreams); 
+	STDMETHODIMP Enable(long lIndex, DWORD dwFlags); 
+	STDMETHODIMP Info(long lIndex, AM_MEDIA_TYPE** ppmt, DWORD* pdwFlags, LCID* plcid, DWORD* pdwGroup, WCHAR** ppszName, IUnknown** ppObject, IUnknown** ppUnk);  
+
+    // CPersistStream
+	STDMETHODIMP GetClassID(CLSID* pClsid);
+
+protected:
+	HRESULT ChangeMediaType(int iPosition);
+
+	HDC m_hdc;
+	HBITMAP m_hbm;
+	HFONT m_hfont;
+	void PrintMessages(BYTE* pOut);
+
+/* ResX2 */
+	CAutoVectorPtr<BYTE> m_pTempPicBuff;
+	HRESULT Copy(BYTE* pSub, BYTE* pIn, CSize sub, CSize in, int bpp, const GUID& subtype, DWORD black);
+
+	// segment start time, absolute time
+	CRefTime m_tPrev;
+	REFERENCE_TIME CalcCurrentTime();
+
+	double m_fps;
+
+	// 3.x- versions of microsoft's mpeg4 codec output flipped image
+	bool m_fMSMpeg4Fix;
+
+	// DivxPlus puts our picture upside-down on the tv
+	bool m_fDivxPlusFix;
+
+	// don't set the "hide subtitles" stream until we are finished with loading
+	bool m_fLoading;
+
+	bool Open();
+
+	int FindPreferedLanguage(bool fHideToo = true);
+	void UpdatePreferedLanguages(CString lang);
+
+	CCritSec m_csSubLock;
+	CInterfaceList<ISubStream> m_pSubStreams;
+	DWORD_PTR m_nSubtitleId;
+	void UpdateSubtitle(bool fApplyDefStyle = true);
+	void SetSubtitle(ISubStream* pSubStream, bool fApplyDefStyle = true);
+	void InvalidateSubtitle(REFERENCE_TIME rtInvalidate = -1, DWORD_PTR nSubtitleId = -1);
+
+	// the text input pin is using these
+	void AddSubStream(ISubStream* pSubStream);
+	void RemoveSubStream(ISubStream* pSubStream);
+	void Post_EC_OLE_EVENT(CString str, DWORD_PTR nSubtitleId = -1);
+
+private:
+	class CFileReloaderData
+	{
+	public:
+		ATL::CEvent EndThreadEvent, RefreshEvent;
+		CAtlList<CString> files;
+		CAtlArray<CTime> mtime;
+	} m_frd;
+
+	void SetupFRD(CStringArray& paths, CAtlArray<HANDLE>& handles);
+	DWORD ThreadProc();
+
+private:
+	HANDLE m_hSystrayThread;
+	SystrayIconData m_tbid;
+};
+
+/* The "auto-loading" version */
+
+[uuid("9852A670-F845-491b-9BE6-EBD841B8A613")]
+class CDirectVobSubFilter2 : public CDirectVobSubFilter
+{
+	bool ShouldWeAutoload(IFilterGraph* pGraph);
+	void GetRidOfInternalScriptRenderer();
+
+public:
+    CDirectVobSubFilter2(LPUNKNOWN punk, HRESULT* phr, const GUID& clsid = __uuidof(CDirectVobSubFilter2));
+
+	HRESULT CheckConnect(PIN_DIRECTION dir, IPin* pPin);
+	STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName);
+    HRESULT CheckInputType(const CMediaType* mtIn);
+};
+
diff --git a/vsfilter/DirectVobSubPropPage.cpp b/vsfilter/DirectVobSubPropPage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..80e71c0377f84893019304f87d856e33a81d7043
--- /dev/null
+++ b/vsfilter/DirectVobSubPropPage.cpp
@@ -0,0 +1,1177 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <commdlg.h>
+#include <afxdlgs.h>
+#include "DirectVobSubFilter.h"
+#include "DirectVobSubPropPage.h"
+#include "VSFilter.h"
+#include "StyleEditorDialog.h"
+
+#include "DSUtil/DSUtil.h"
+#include "DSUtil/MediaTypes.h"
+
+BOOL WINAPI MyGetDialogSize(int iResourceID, DLGPROC pDlgProc, LPARAM lParam, SIZE* pResult)
+{
+    HWND hwnd = CreateDialogParam(AfxGetResourceHandle(),
+        MAKEINTRESOURCE(iResourceID),
+        GetDesktopWindow(),
+        pDlgProc,
+        lParam);
+    
+	if(hwnd == NULL) return FALSE;
+
+    RECT rc;
+    GetWindowRect(hwnd, &rc);
+    pResult->cx = rc.right - rc.left;
+    pResult->cy = rc.bottom - rc.top;
+
+    DestroyWindow(hwnd);
+
+    return TRUE;
+}
+
+STDMETHODIMP CDVSBasePPage::GetPageInfo(LPPROPPAGEINFO pPageInfo)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CString str;
+	if(!str.LoadString(m_TitleId)) return E_FAIL;
+
+	WCHAR wszTitle[STR_MAX_LENGTH];
+#ifdef UNICODE
+	wcscpy(wszTitle, str);
+#else
+	mbstowcs(wszTitle, str, str.GetLength()+1);
+#endif
+
+	CheckPointer(pPageInfo, E_POINTER);
+
+	// Allocate dynamic memory for the property page title
+
+	LPOLESTR pszTitle;
+	HRESULT hr = AMGetWideString(wszTitle, &pszTitle);
+	if(FAILED(hr)) {NOTE("No caption memory"); return hr;}
+
+	pPageInfo->cb               = sizeof(PROPPAGEINFO);
+	pPageInfo->pszTitle         = pszTitle;
+	pPageInfo->pszDocString     = NULL;
+	pPageInfo->pszHelpFile      = NULL;
+	pPageInfo->dwHelpContext    = 0;
+	// Set defaults in case GetDialogSize fails
+	pPageInfo->size.cx          = 340;
+	pPageInfo->size.cy          = 150;
+
+	MyGetDialogSize(m_DialogId, DialogProc, 0L, &pPageInfo->size);
+
+	return NOERROR;
+}
+
+STDMETHODIMP CDVSBasePPage::Activate(HWND hwndParent, LPCRECT pRect, BOOL fModal)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CheckPointer(pRect,E_POINTER);
+/*
+    // Return failure if SetObject has not been called.
+    if (m_bObjectSet == FALSE) {
+        return E_UNEXPECTED;
+    }
+*/
+    if(m_hwnd) return E_UNEXPECTED;
+
+    m_hwnd = CreateDialogParam(AfxGetResourceHandle(), MAKEINTRESOURCE(m_DialogId), hwndParent, DialogProc, (LPARAM)this);
+    if(m_hwnd == NULL) return E_OUTOFMEMORY;
+
+    OnActivate();
+    Move(pRect);
+    return Show(SW_SHOWNORMAL);
+}
+
+/* CDVSBasePPage */
+
+CDVSBasePPage::CDVSBasePPage(TCHAR* pName, LPUNKNOWN lpunk, int DialogId, int TitleId) :
+    CBasePropertyPage(pName, lpunk, DialogId, TitleId),
+    m_bIsInitialized(FALSE),
+	m_fAttached(false),
+	m_fDisableInstantUpdate(false)
+{
+}
+
+BOOL CDVSBasePPage::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			if(m_bIsInitialized)
+			{
+				m_bDirty = TRUE;
+				if(m_pPageSite) m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
+
+				switch(HIWORD(wParam))
+				{
+					case BN_CLICKED: 
+					case CBN_SELCHANGE:
+					case EN_CHANGE:
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+						if(!m_fDisableInstantUpdate 
+						&& !(HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDC_INSTANTUPDATE)
+						&& !!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_INSTANTUPDATE), 0)) 
+							OnApplyChanges();
+					}
+				}
+			}
+		}
+		break;
+
+		case WM_NCDESTROY:
+			DetachControls();
+		break;
+	}
+
+	return OnMessage(uMsg, wParam, lParam) 
+		? 0 
+		: CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
+}
+
+HRESULT CDVSBasePPage::OnConnect(IUnknown* pUnknown)
+{
+    if(!(m_pDirectVobSub = pUnknown)) return E_NOINTERFACE;
+
+	m_pDirectVobSub->LockSubtitleReloader(true); // *
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	UpdateObjectData(false);
+
+	m_bIsInitialized = FALSE;
+
+    return NOERROR;
+}
+
+HRESULT CDVSBasePPage::OnDisconnect()
+{
+    if(m_pDirectVobSub == NULL) return E_UNEXPECTED;
+
+	m_pDirectVobSub->LockSubtitleReloader(false); // *
+
+	// for some reason OnDisconnect() will be called twice, that's why we 
+	// need to release m_pDirectVobSub manually on the first call to avoid 
+	// a second "m_pDirectVobSub->LockSubtitleReloader(false);"
+
+	m_pDirectVobSub.Release(); 
+
+	return NOERROR;
+}
+
+HRESULT CDVSBasePPage::OnActivate()
+{
+	ASSERT(m_pDirectVobSub);
+
+	AttachControls();
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	UpdateControlData(false);
+
+	m_bIsInitialized = TRUE;
+
+    return NOERROR;
+}
+
+HRESULT CDVSBasePPage::OnDeactivate()
+{
+	ASSERT(m_pDirectVobSub);
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	UpdateControlData(true);
+
+    m_bIsInitialized = FALSE;
+
+    return NOERROR;
+}
+
+HRESULT CDVSBasePPage::OnApplyChanges()
+{
+    ASSERT(m_pDirectVobSub);
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	if(m_bIsInitialized)
+	{
+		OnDeactivate();
+        UpdateObjectData(true);
+		m_pDirectVobSub->UpdateRegistry(); // *
+		OnActivate();
+	}
+
+    return NOERROR;
+}
+
+void CDVSBasePPage::AttachControls()
+{
+	DetachControls();
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	POSITION pos = m_controls.GetStartPosition();
+	while(pos)
+	{
+		UINT id;
+		CWnd* pControl;
+		m_controls.GetNextAssoc(pos, id, pControl);
+		if(pControl) 
+		{
+			BOOL fRet = pControl->Attach(GetDlgItem(m_Dlg, id));
+			ASSERT(fRet);
+		}
+	}
+
+	m_fAttached = true;
+}
+
+void CDVSBasePPage::DetachControls()
+{
+	if(!m_fAttached) return;
+
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	POSITION pos = m_controls.GetStartPosition();
+	while(pos)
+	{
+		UINT id;
+		CWnd* pControl;
+		m_controls.GetNextAssoc(pos, id, pControl);
+		if(pControl) pControl->Detach();
+	}
+
+	m_fAttached = false;
+}
+
+void CDVSBasePPage::BindControl(UINT id, CWnd& control)
+{
+	m_controls[id] = &control;
+}
+
+/* CDVSMainPPage */
+
+CDVSMainPPage::CDVSMainPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Property Page (main)"), pUnk, IDD_DVSMAINPAGE, IDD_DVSMAINPAGE),
+	m_nLangs(0),
+	m_ppLangs(NULL)
+{
+	BindControl(IDC_FILENAME, m_fnedit);
+	BindControl(IDC_LANGCOMBO, m_langs);
+	BindControl(IDC_OVERRIDEPLACEMENT, m_oplacement);
+	BindControl(IDC_SPIN1, m_subposx);
+	BindControl(IDC_SPIN2, m_subposy);
+	BindControl(IDC_FONT, m_font);
+	BindControl(IDC_ONLYSHOWFORCEDSUBS, m_forcedsubs);
+}
+
+CDVSMainPPage::~CDVSMainPPage()
+{
+	FreeLangs();
+}
+
+void CDVSMainPPage::FreeLangs()
+{
+	if(m_nLangs > 0 && m_ppLangs) 
+	{
+		for(int i = 0; i < m_nLangs; i++) CoTaskMemFree(m_ppLangs[i]);
+		CoTaskMemFree(m_ppLangs);
+		m_nLangs = 0;
+		m_ppLangs = NULL;
+	}
+}
+
+void CDVSMainPPage::AllocLangs(int nLangs)
+{
+	m_ppLangs = (WCHAR**)CoTaskMemRealloc(m_ppLangs, sizeof(WCHAR*)*nLangs);
+	m_nLangs = nLangs;
+}
+
+bool CDVSMainPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case BN_CLICKED:
+				{
+					if(LOWORD(wParam) == IDC_OPEN)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+						CFileDialog fd(TRUE, NULL, NULL, 
+							OFN_EXPLORER|OFN_ENABLESIZING|OFN_HIDEREADONLY|OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST,
+							_T(".idx .smi .sub .srt .psb .ssa .ass .usf .ssf|*.idx;*.smi;*.sub;*.srt;*.psb;*.ssa;*.ass;*.usf;*.ssf|")
+							_T("All files (*.*)|*.*||"),
+							CDialog::FromHandle(m_Dlg), 0);
+
+						if(fd.DoModal() == IDOK)
+						{
+							m_fnedit.SetWindowText(fd.GetPathName());
+						}
+
+						return(true);
+					}
+					else if(LOWORD(wParam) == IDC_FONT)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+						CStyleEditorDialog dlg(_T("Default"), &m_defStyle, CWnd::FromHandle(m_hwnd));
+
+						if(dlg.DoModal() == IDOK)
+						{
+							m_defStyle = dlg.m_stss;
+							CString str = m_defStyle.fontName;
+							if(str.GetLength() > 18) str = str.Left(16).TrimRight() + _T("...");
+							m_font.SetWindowText(str);
+						}
+
+						return(true);
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+	return(false);
+}
+
+void CDVSMainPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		if(m_pDirectVobSub->put_FileName(m_fn) == S_OK)
+		{
+			int nLangs;
+			m_pDirectVobSub->get_LanguageCount(&nLangs); 
+			AllocLangs(nLangs);
+			for(int i = 0; i < m_nLangs; i++) m_pDirectVobSub->get_LanguageName(i, &m_ppLangs[i]);
+			m_pDirectVobSub->get_SelectedLanguage(&m_iSelectedLanguage);
+		}
+
+		m_pDirectVobSub->put_SelectedLanguage(m_iSelectedLanguage);
+		m_pDirectVobSub->put_Placement(m_fOverridePlacement, m_PlacementXperc, m_PlacementYperc);
+		m_pDirectVobSub->put_VobSubSettings(true, m_fOnlyShowForcedVobSubs, false);
+		m_pDirectVobSub->put_TextSettings(&m_defStyle);
+	}
+	else
+	{
+		m_pDirectVobSub->get_FileName(m_fn);
+		int nLangs;
+		m_pDirectVobSub->get_LanguageCount(&nLangs); 
+		AllocLangs(nLangs);
+		for(int i = 0; i < m_nLangs; i++) m_pDirectVobSub->get_LanguageName(i, &m_ppLangs[i]);
+		m_pDirectVobSub->get_SelectedLanguage(&m_iSelectedLanguage);
+		m_pDirectVobSub->get_Placement(&m_fOverridePlacement, &m_PlacementXperc, &m_PlacementYperc);
+		m_pDirectVobSub->get_VobSubSettings(NULL, &m_fOnlyShowForcedVobSubs, NULL);
+		m_pDirectVobSub->get_TextSettings(&m_defStyle);
+	}
+}
+
+void CDVSMainPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		CString fn;
+		m_fnedit.GetWindowText(fn);
+#ifdef UNICODE
+		wcscpy(m_fn, fn);
+#else
+		mbstowcs(m_fn, fn, fn.GetLength()+1);
+#endif
+		m_iSelectedLanguage = m_langs.GetCurSel();
+		m_fOverridePlacement = !!m_oplacement.GetCheck();
+		m_PlacementXperc = m_subposx.GetPos();
+		m_PlacementYperc = m_subposy.GetPos();
+		m_fOnlyShowForcedVobSubs = !!m_forcedsubs.GetCheck();
+	}
+	else
+	{
+		m_fnedit.SetWindowText(CString(m_fn));
+		m_oplacement.SetCheck(m_fOverridePlacement);
+		m_subposx.SetRange(-20, 120);
+		m_subposx.SetPos(m_PlacementXperc);
+		m_subposx.EnableWindow(m_fOverridePlacement);
+		m_subposy.SetRange(-20, 120);
+		m_subposy.SetPos(m_PlacementYperc);
+		m_subposy.EnableWindow(m_fOverridePlacement);
+		m_font.SetWindowText(m_defStyle.fontName);
+		m_forcedsubs.SetCheck(m_fOnlyShowForcedVobSubs);
+		m_langs.ResetContent();
+		m_langs.EnableWindow(m_nLangs > 0);
+		for(int i = 0; i < m_nLangs; i++) m_langs.AddString(CString(m_ppLangs[i]));
+		m_langs.SetCurSel(m_iSelectedLanguage);
+	}
+}
+
+/* CDVSGeneralPPage */
+
+CDVSGeneralPPage::CDVSGeneralPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Property Page (global settings)"), pUnk, IDD_DVSGENERALPAGE, IDD_DVSGENERALPAGE)
+{
+	BindControl(IDC_VEREXTCOMBO, m_verext);
+	BindControl(IDC_MOD32FIX, m_mod32fix);
+	BindControl(IDC_RESX2COMBO, m_resx2);
+	BindControl(IDC_SPIN3, m_resx2w);
+	BindControl(IDC_SPIN4, m_resx2h);
+	BindControl(IDC_LOADCOMBO, m_load);
+	BindControl(IDC_EXTLOAD, m_extload);
+	BindControl(IDC_WEBLOAD, m_webload);
+	BindControl(IDC_EMBLOAD, m_embload);
+}
+
+bool CDVSGeneralPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case CBN_SELCHANGE:
+				{
+					AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+					if(LOWORD(wParam) == IDC_RESX2COMBO)
+					{
+						m_resx2w.EnableWindow(m_resx2.GetCurSel() == 2);
+						m_resx2h.EnableWindow(m_resx2.GetCurSel() == 2);
+						return(true);
+					}
+					else if(LOWORD(wParam) == IDC_LOADCOMBO)
+					{
+						m_extload.EnableWindow(m_load.GetCurSel() == 1);
+						m_webload.EnableWindow(m_load.GetCurSel() == 1);
+						m_embload.EnableWindow(m_load.GetCurSel() == 1);
+						return(true);
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+	return(false);
+}
+
+void CDVSGeneralPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		m_pDirectVobSub->put_ExtendPicture(m_HorExt, m_VerExt, m_ResX2, m_ResX2minw, m_ResX2minh);
+		m_pDirectVobSub->put_LoadSettings(m_LoadLevel, m_fExternalLoad, m_fWebLoad, m_fEmbeddedLoad);
+	}
+	else
+	{
+		m_pDirectVobSub->get_ExtendPicture(&m_HorExt, &m_VerExt, &m_ResX2, &m_ResX2minw, &m_ResX2minh);
+		m_pDirectVobSub->get_LoadSettings(&m_LoadLevel, &m_fExternalLoad, &m_fWebLoad, &m_fEmbeddedLoad);
+	}
+}
+
+void CDVSGeneralPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		if(m_verext.GetCurSel() >= 0) m_VerExt = m_verext.GetItemData(m_verext.GetCurSel());
+		m_HorExt = !!m_mod32fix.GetCheck();
+		if(m_resx2.GetCurSel() >= 0) m_ResX2 = m_resx2.GetItemData(m_resx2.GetCurSel());
+		m_ResX2minw = m_resx2w.GetPos(); 
+		m_ResX2minh = m_resx2h.GetPos();
+		if(m_load.GetCurSel() >= 0) m_LoadLevel = m_load.GetItemData(m_load.GetCurSel());
+		m_fExternalLoad = !!m_extload.GetCheck();
+		m_fWebLoad = !!m_webload.GetCheck();
+		m_fEmbeddedLoad = !!m_embload.GetCheck();
+	}
+	else
+	{
+		m_verext.ResetContent();
+		m_verext.AddString(ResStr(IDS_ORGHEIGHT)); m_verext.SetItemData(0, 0);
+		m_verext.AddString(ResStr(IDS_EXTTO169)); m_verext.SetItemData(1, 1);
+		m_verext.AddString(ResStr(IDS_EXTTO43)); m_verext.SetItemData(2, 2);
+		m_verext.AddString(ResStr(IDS_EXTTO480)); m_verext.SetItemData(3, 3);
+		m_verext.AddString(ResStr(IDS_EXTTO576)); m_verext.SetItemData(4, 4);
+		m_verext.AddString(ResStr(IDS_CROPTO169)); m_verext.SetItemData(5, 0x81);
+		m_verext.AddString(ResStr(IDS_CROPTO43)); m_verext.SetItemData(6, 0x82);
+		m_verext.SetCurSel((m_VerExt&0x7f) + ((m_VerExt&0x80)?4:0));
+		m_mod32fix.SetCheck(m_HorExt&1);
+		m_resx2.ResetContent();
+		m_resx2.AddString(ResStr(IDS_ORGRES)); m_resx2.SetItemData(0, 0);
+		m_resx2.AddString(ResStr(IDS_DBLRES)); m_resx2.SetItemData(1, 1);
+		m_resx2.AddString(ResStr(IDS_DBLRESIF)); m_resx2.SetItemData(2, 2);
+		m_resx2.SetCurSel(m_ResX2);
+		m_resx2w.SetRange(0, 2048);
+		m_resx2w.SetPos(m_ResX2minw);
+		m_resx2w.EnableWindow(m_ResX2 == 2);
+		m_resx2h.SetRange(0, 2048);
+		m_resx2h.SetPos(m_ResX2minh);
+		m_resx2h.EnableWindow(m_ResX2 == 2);
+		m_load.ResetContent();
+		m_load.AddString(ResStr(IDS_DONOTLOAD)); m_load.SetItemData(0, 2);
+		m_load.AddString(ResStr(IDS_LOADWHENNEEDED)); m_load.SetItemData(1, 0);
+		m_load.AddString(ResStr(IDS_ALWAYSLOAD)); m_load.SetItemData(2, 1);
+		m_load.SetCurSel(!m_LoadLevel?1:m_LoadLevel==1?2:0);
+		m_extload.SetCheck(m_fExternalLoad);
+		m_webload.SetCheck(m_fWebLoad);
+		m_embload.SetCheck(m_fEmbeddedLoad);
+		m_extload.EnableWindow(m_load.GetCurSel() == 1);
+		m_webload.EnableWindow(m_load.GetCurSel() == 1);
+		m_embload.EnableWindow(m_load.GetCurSel() == 1);
+	}
+}
+
+/* CDVSMiscPPage */
+
+CDVSMiscPPage::CDVSMiscPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Property Page (misc settings)"), pUnk, IDD_DVSMISCPAGE, IDD_DVSMISCPAGE)
+{
+	BindControl(IDC_FLIP, m_flippic);
+	BindControl(IDC_FLIPSUB, m_flipsub);
+	BindControl(IDC_HIDE, m_hidesub);
+	BindControl(IDC_SHOWOSDSTATS, m_showosd);
+	BindControl(IDC_PREBUFFERING, m_prebuff);
+	BindControl(IDC_AUTORELOAD, m_autoreload);
+	BindControl(IDC_SAVEFULLPATH, m_savefullpath);
+	BindControl(IDC_INSTANTUPDATE, m_instupd);
+}
+
+bool CDVSMiscPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case BN_CLICKED:
+				{
+					if(LOWORD(wParam) == IDC_INSTANTUPDATE)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+						theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_INSTANTUPDATE), !!m_instupd.GetCheck());
+						return(true);
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+    return(false);
+}
+
+void CDVSMiscPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		m_pDirectVobSub->put_Flip(m_fFlipPicture, m_fFlipSubtitles);
+		m_pDirectVobSub->put_HideSubtitles(m_fHideSubtitles);
+		m_pDirectVobSub->put_OSD(m_fOSD);
+		m_pDirectVobSub->put_PreBuffering(m_fDoPreBuffering);
+		m_pDirectVobSub->put_SubtitleReloader(m_fReloaderDisabled);
+		m_pDirectVobSub->put_SaveFullPath(m_fSaveFullPath);
+	}
+	else
+	{
+		m_pDirectVobSub->get_Flip(&m_fFlipPicture, &m_fFlipSubtitles);
+		m_pDirectVobSub->get_HideSubtitles(&m_fHideSubtitles);
+		m_pDirectVobSub->get_OSD(&m_fOSD);
+		m_pDirectVobSub->get_PreBuffering(&m_fDoPreBuffering);
+		m_pDirectVobSub->get_SubtitleReloader(&m_fReloaderDisabled);
+		m_pDirectVobSub->get_SaveFullPath(&m_fSaveFullPath);
+	}
+}
+
+void CDVSMiscPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		m_fFlipPicture = !!m_flippic.GetCheck();
+		m_fFlipSubtitles = !!m_flipsub.GetCheck();
+		m_fHideSubtitles = !!m_hidesub.GetCheck();
+		m_fSaveFullPath = !!m_savefullpath.GetCheck();
+		m_fDoPreBuffering = !!m_prebuff.GetCheck();
+		m_fOSD = !!m_showosd.GetCheck();
+		m_fReloaderDisabled = !m_autoreload.GetCheck();
+	}
+	else
+	{
+		m_flippic.SetCheck(m_fFlipPicture);
+		m_flipsub.SetCheck(m_fFlipSubtitles);
+		m_hidesub.SetCheck(m_fHideSubtitles);
+		m_savefullpath.SetCheck(m_fSaveFullPath);
+		m_prebuff.SetCheck(m_fDoPreBuffering);
+		m_showosd.SetCheck(m_fOSD);
+		m_autoreload.SetCheck(!m_fReloaderDisabled);
+		m_instupd.SetCheck(!!theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_INSTANTUPDATE), 0));
+	}
+}
+
+/* CDVSTimingPPage */
+
+CDVSTimingPPage::CDVSTimingPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Timing Property Page"), pUnk, IDD_DVSTIMINGPAGE, IDD_DVSTIMINGPAGE)
+{
+	BindControl(IDC_MODFPS, m_modfps);
+	BindControl(IDC_FPS, m_fps);
+	BindControl(IDC_SPIN5, m_subdelay);
+	BindControl(IDC_SPIN6, m_subspeedmul);
+	BindControl(IDC_SPIN9, m_subspeeddiv);
+}
+
+bool CDVSTimingPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case BN_CLICKED:
+				{
+					if(LOWORD(wParam) == IDC_MODFPS)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+						m_fps.EnableWindow(!!m_modfps.GetCheck());
+						return(true);
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+	return(false);
+}
+
+void CDVSTimingPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		m_pDirectVobSub->put_SubtitleTiming(m_SubtitleDelay, m_SubtitleSpeedMul, m_SubtitleSpeedDiv);
+		m_pDirectVobSub->put_MediaFPS(m_fMediaFPSEnabled, m_MediaFPS);
+	}
+	else
+	{
+		m_pDirectVobSub->get_SubtitleTiming(&m_SubtitleDelay, &m_SubtitleSpeedMul, &m_SubtitleSpeedDiv);
+		m_pDirectVobSub->get_MediaFPS(&m_fMediaFPSEnabled, &m_MediaFPS);
+	}
+}
+
+void CDVSTimingPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		m_fMediaFPSEnabled = !!m_modfps.GetCheck();
+		CString fpsstr;
+		m_fps.GetWindowText(fpsstr);
+		float fps;
+		if(_stscanf(fpsstr, _T("%f"), &fps) == 1) m_MediaFPS = fps;
+#if _MFC_VER >= 0x0700
+		m_SubtitleDelay = m_subdelay.GetPos32();
+		m_SubtitleSpeedMul = m_subspeedmul.GetPos32();
+		m_SubtitleSpeedDiv = m_subspeeddiv.GetPos32();
+#else
+		m_SubtitleDelay = SendMessage(GetDlgItem(m_Dlg, IDC_SPIN5), UDM_GETPOS32, 0, 0);
+		m_SubtitleSpeedMul = SendMessage(GetDlgItem(m_Dlg, IDC_SPIN6), UDM_GETPOS32, 0, 0);
+		m_SubtitleSpeedDiv = SendMessage(GetDlgItem(m_Dlg, IDC_SPIN9), UDM_GETPOS32, 0, 0);
+#endif
+	}
+	else
+	{
+		m_modfps.SetCheck(m_fMediaFPSEnabled);
+		CString fpsstr;
+		fpsstr.Format(_T("%.4f"), m_MediaFPS);
+		m_fps.SetWindowText(fpsstr);
+		m_fps.EnableWindow(m_fMediaFPSEnabled);
+		m_subdelay.SetRange32(-180*60*1000, 180*60*1000);
+		m_subspeedmul.SetRange32(0, 1000000);
+		m_subspeeddiv.SetRange32(1, 1000000);
+#if _MFC_VER >= 0x0700
+		m_subdelay.SetPos32(m_SubtitleDelay);
+		m_subspeedmul.SetPos32(m_SubtitleSpeedMul);
+		m_subspeeddiv.SetPos32(m_SubtitleSpeedDiv);
+#else
+		SendMessage(GetDlgItem(m_Dlg, IDC_SPIN5), UDM_SETPOS32, 0, (LPARAM)m_SubtitleDelay);
+		SendMessage(GetDlgItem(m_Dlg, IDC_SPIN6), UDM_SETPOS32, 0, (LPARAM)m_SubtitleSpeedMul);
+		SendMessage(GetDlgItem(m_Dlg, IDC_SPIN9), UDM_SETPOS32, 0, (LPARAM)m_SubtitleSpeedDiv);
+#endif
+	}
+}
+
+/* CDVSAboutPPage */
+
+CDVSAboutPPage::CDVSAboutPPage(LPUNKNOWN lpunk, HRESULT* phr) : 
+	CDVSBasePPage(NAME("About Property Page"), lpunk, IDD_DVSABOUTPAGE, IDD_DVSABOUTPAGE)
+{
+
+}
+
+bool CDVSAboutPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case BN_CLICKED:
+				{
+					if(LOWORD(wParam) == IDC_HOMEPAGEBTN)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+						ShellExecute(m_Dlg, _T("open"), ResStr(IDS_URL_HOMEPAGE), NULL, NULL, SW_SHOWNORMAL);
+						return(true);
+					}
+					else if(LOWORD(wParam) == IDC_BUGREPORTBTN)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+						ShellExecute(m_Dlg, _T("open"), ResStr(IDS_URL_EMAIL), NULL, NULL, SW_SHOWNORMAL);
+						return(true);
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+    return(false);
+}
+
+/* CDVSZoomPPage */
+
+CDVSZoomPPage::CDVSZoomPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Zoom Property Page"), pUnk, IDD_DVSZOOMPAGE, IDD_DVSZOOMPAGE)
+{
+	BindControl(IDC_SPIN1, m_posx);
+	BindControl(IDC_SPIN2, m_posy);
+	BindControl(IDC_SPIN7, m_scalex);
+	BindControl(IDC_SPIN8, m_scaley);
+}
+
+bool CDVSZoomPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case EN_CHANGE:
+				{
+					if(LOWORD(wParam) == IDC_EDIT1 || LOWORD(wParam) == IDC_EDIT2
+					|| LOWORD(wParam) == IDC_EDIT7 || LOWORD(wParam) == IDC_EDIT8)
+					{
+						AFX_MANAGE_STATE(AfxGetStaticModuleState());
+						UpdateControlData(true);
+						UpdateObjectData(true);
+						return(true);
+					}
+				}
+
+				break;
+			}
+		}
+		break;
+	}
+
+	return(false);
+}
+
+void CDVSZoomPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		m_rect.left = 1.0f * (short)m_posx.GetPos() / 100;
+		m_rect.top = 1.0f * (short)m_posy.GetPos() / 100;
+		m_rect.right = m_rect.left + 1.0f * (short)m_scalex.GetPos() / 100;
+		m_rect.bottom = m_rect.top + 1.0f * (short)m_scaley.GetPos() / 100;
+	}
+	else
+	{
+		m_posx.SetRange(-100, 100);
+		m_posx.SetPos((int)(m_rect.left*100));
+		m_posy.SetRange(-100, 100);
+		m_posy.SetPos((int)(m_rect.top*100));
+		m_scalex.SetRange(-300, 300);
+		m_scalex.SetPos((int)((m_rect.right-m_rect.left)*100));
+		m_scaley.SetRange(-300, 300);
+		m_scaley.SetPos((int)((m_rect.bottom-m_rect.top)*100));
+	}
+}
+
+void CDVSZoomPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		m_pDirectVobSub->put_ZoomRect(&m_rect);
+	}
+	else
+	{
+		m_pDirectVobSub->get_ZoomRect(&m_rect);
+	}
+}
+
+// TODO: Make CDVSColorPPage and CDVSPathsPPage use an interface on DirectVobSub instead of the registry to communicate
+
+/* CDVSColorPPage */
+
+CDVSColorPPage::CDVSColorPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Color Property Page"), pUnk, IDD_DVSCOLORPAGE, IDD_DVSCOLORPAGE)
+{
+	BindControl(IDC_PREFLIST, m_preflist);
+	BindControl(IDC_DYNCHGLIST, m_dynchglist);
+	BindControl(IDC_FORCERGBCHK, m_forcergb);
+
+	m_fDisableInstantUpdate = true;
+}
+
+bool CDVSColorPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case LBN_DBLCLK:
+					if((HWND)lParam == m_dynchglist.m_hWnd)
+					{
+						int old = -1; 
+						m_pDirectVobSub->get_ColorFormat(&old);
+						if(FAILED(m_pDirectVobSub->put_ColorFormat(m_dynchglist.GetCurSel())))
+							m_dynchglist.SetCurSel(old);
+
+						return(true);
+					}
+				break;
+
+				case BN_CLICKED:
+				{
+					switch(LOWORD(wParam))
+					{
+						case IDC_COLORCHANGE:
+						{
+							int old = -1; 
+							m_pDirectVobSub->get_ColorFormat(&old);
+							if(FAILED(m_pDirectVobSub->put_ColorFormat(m_dynchglist.GetCurSel())))
+								m_dynchglist.SetCurSel(old);
+
+							return(true);
+						}
+						case IDC_COLORUP:
+						{
+							int sel = m_preflist.GetCurSel();
+							if(sel > 0)
+							{
+								CString str;
+								m_preflist.GetText(sel, str);
+								int iPos = (int)m_preflist.GetItemData(sel);
+								m_preflist.DeleteString(sel);
+								sel--;
+								m_preflist.InsertString(sel, str);
+								m_preflist.SetItemData(sel, iPos);
+								m_preflist.SetCurSel(sel);
+							}
+
+							return(true);
+						}
+						case IDC_COLORDOWN:
+						{
+							int sel = m_preflist.GetCurSel();
+							if(sel >= 0 && sel < m_preflist.GetCount()-1)
+							{
+								CString str;
+								m_preflist.GetText(sel, str);
+								int iPos = (int)m_preflist.GetItemData(sel);
+								m_preflist.DeleteString(sel);
+								sel++;
+								m_preflist.InsertString(sel, str);
+								m_preflist.SetItemData(sel, iPos);
+								m_preflist.SetCurSel(sel);
+							}
+
+							return(true);
+						}
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+    return(false);
+}
+
+void CDVSColorPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+	}
+	else
+	{
+	}
+}
+
+void CDVSColorPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		if(m_preflist.GetCount() == VIHSIZE)
+		{
+			BYTE* pData = new BYTE[VIHSIZE];
+
+			for(int i = 0; i < m_preflist.GetCount(); i++)
+				pData[i] = (BYTE)m_preflist.GetItemData(i);
+
+			theApp.WriteProfileBinary(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_COLORFORMATS), pData, VIHSIZE);
+
+			delete [] pData;
+		}
+		else ASSERT(0);
+
+		theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FORCERGB), !!m_forcergb.GetCheck());
+	}
+	else
+	{
+		m_preflist.ResetContent();
+		m_dynchglist.ResetContent();
+
+		BYTE* pData = NULL;
+		UINT nSize;
+		
+		if(!theApp.GetProfileBinary(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_COLORFORMATS), &pData, &nSize)
+		|| !pData || nSize != VIHSIZE)
+		{
+			if(pData) delete [] pData, pData = NULL;
+
+			nSize = VIHSIZE;
+			pData = new BYTE[VIHSIZE];
+			for(int i = 0; i < VIHSIZE; i++) pData[i] = i;
+		}
+
+		if(pData)
+		{
+			for(int i = 0; i < (int)nSize; i++)
+			{
+				m_dynchglist.AddString(VIH2String(pData[i]));
+				m_dynchglist.SetItemData(i, pData[i]);
+				m_preflist.AddString(VIH2String(pData[i]));
+				m_preflist.SetItemData(i, pData[i]);
+			}
+
+			int iPosition = -1;
+			m_pDirectVobSub->get_ColorFormat(&iPosition);
+			m_dynchglist.SetCurSel(iPosition);
+
+			delete [] pData;
+		}
+
+		m_forcergb.SetCheck(theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_FORCERGB), 0)?BST_CHECKED:BST_UNCHECKED);
+	}
+}
+
+/* CDVSPathsPPage */
+
+CDVSPathsPPage::CDVSPathsPPage(LPUNKNOWN pUnk, HRESULT* phr) :
+    CDVSBasePPage(NAME("DirectVobSub Paths Property Page"), pUnk, IDD_DVSPATHSPAGE, IDD_DVSPATHSPAGE)
+{
+	BindControl(IDC_PATHLIST, m_pathlist);
+	BindControl(IDC_PATHEDIT, m_path);
+	BindControl(IDC_BROWSE, m_browse);
+	BindControl(IDC_REMOVE, m_remove);
+	BindControl(IDC_ADD, m_add);
+
+	m_fDisableInstantUpdate = true;
+}
+
+bool CDVSPathsPPage::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch(uMsg)
+    {
+        case WM_COMMAND:
+        {
+			switch(HIWORD(wParam))
+			{
+				case LBN_SELCHANGE:
+					if((HWND)lParam == m_pathlist.m_hWnd)
+					{
+						int i = m_pathlist.GetCurSel();
+						m_remove.EnableWindow(i >= 3 ? TRUE : FALSE);
+						if(i >= 0)
+						{
+							CString path;
+							m_pathlist.GetText(i, path);
+							m_path.SetWindowText(path);
+						}
+						return(true);
+					}
+				break;
+
+				case LBN_SELCANCEL:
+					if((HWND)lParam == m_pathlist.m_hWnd)
+					{
+						m_remove.EnableWindow(FALSE);
+						return(true);
+					}
+				break;
+
+				case BN_CLICKED:
+				{
+					switch(LOWORD(wParam))
+					{
+						case IDC_BROWSE:
+						{
+							TCHAR pathbuff[MAX_PATH];
+
+							BROWSEINFO bi;
+							bi.hwndOwner = m_Dlg;
+							bi.pidlRoot = NULL;
+							bi.pszDisplayName = pathbuff;
+							bi.lpszTitle = _T("");
+							bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX | BIF_VALIDATE | BIF_USENEWUI;
+							bi.lpfn = NULL;
+							bi.lParam = 0;
+							bi.iImage = 0; 
+
+							LPITEMIDLIST iil;
+							if(iil = SHBrowseForFolder(&bi))
+							{
+								SHGetPathFromIDList(iil, pathbuff);
+								m_path.SetWindowText(pathbuff);
+							}
+
+							return(true);
+						}
+						break;
+
+						case IDC_REMOVE:
+						{
+							int i = m_pathlist.GetCurSel();
+							if(i >= 0)
+							{
+								m_pathlist.DeleteString(i);
+								i = min(i, m_pathlist.GetCount()-1);
+								if(i >= 0 && m_pathlist.GetCount() > 0)
+								{
+									m_pathlist.SetCurSel(i);
+									m_remove.EnableWindow(i >= 3 ? TRUE : FALSE);
+								}
+							}
+
+							return(true);
+						}
+						break;
+
+						case IDC_ADD:
+						{
+							CString path;
+							m_path.GetWindowText(path);
+							if(!path.IsEmpty() && m_pathlist.FindString(-1, path) < 0)
+								m_pathlist.AddString(path);
+
+							return(true);
+						}
+						break;
+					}
+				}
+				break;
+			}
+		}
+		break;
+	}
+
+    return(false);
+}
+
+void CDVSPathsPPage::UpdateObjectData(bool fSave)
+{
+	if(fSave)
+	{
+		CString chk(_T("123456789")), path, tmp;
+		int i = 0;
+		do
+		{
+			tmp.Format(ResStr(IDS_RP_PATH), i++);
+			path = theApp.GetProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp, chk);
+			if(path != chk) theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp, _T(""));
+		}
+		while(path != chk);
+
+		for(i = 0; i < m_paths.GetSize(); i++)
+		{
+			tmp.Format(ResStr(IDS_RP_PATH), i);
+			theApp.WriteProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp, m_paths[i]);
+		}
+	}
+	else
+	{
+		CString chk(_T("123456789")), path, tmp;
+		int i = 0;
+		do
+		{
+			if(!path.IsEmpty()) m_paths.Add(path);
+			tmp.Format(ResStr(IDS_RP_PATH), i++);
+			path = theApp.GetProfileString(ResStr(IDS_R_DEFTEXTPATHES), tmp, chk);
+		}
+		while(path != chk);
+	}
+}
+
+void CDVSPathsPPage::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		m_paths.RemoveAll();
+		for(int i = 0; i < m_pathlist.GetCount(); i++) 
+		{
+			CString path;
+			m_pathlist.GetText(i, path);
+			m_paths.Add(path);
+		}
+	}
+	else
+	{
+		m_pathlist.ResetContent();
+		for(int i = 0; i < m_paths.GetSize(); i++) 
+			m_pathlist.AddString(m_paths[i]);
+
+		m_remove.EnableWindow(FALSE);
+		m_add.EnableWindow(TRUE);
+	}
+}
diff --git a/vsfilter/DirectVobSubPropPage.h b/vsfilter/DirectVobSubPropPage.h
new file mode 100644
index 0000000000000000000000000000000000000000..db47c211c91957a903ac8a218e7a23b244186e95
--- /dev/null
+++ b/vsfilter/DirectVobSubPropPage.h
@@ -0,0 +1,207 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <afxcmn.h>
+#include "IDirectVobSub.h"
+
+class CDVSBasePPage : public CBasePropertyPage
+{
+public:
+	// we have to override these to use external, resource-only dlls
+	STDMETHODIMP GetPageInfo(LPPROPPAGEINFO pPageInfo);
+	STDMETHODIMP Activate(HWND hwndParent, LPCRECT pRect, BOOL fModal);
+
+protected:
+	CComQIPtr<IDirectVobSub2> m_pDirectVobSub;
+
+	virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) {return(false);}
+	virtual void UpdateObjectData(bool fSave) {}
+	virtual void UpdateControlData(bool fSave) {}
+
+protected:
+    CDVSBasePPage(TCHAR* pName, LPUNKNOWN lpunk, int DialogId, int TitleId);
+
+	bool m_fDisableInstantUpdate;
+
+private:
+    BOOL m_bIsInitialized;
+
+    HRESULT OnConnect(IUnknown* pUnknown), OnDisconnect(), OnActivate(), OnDeactivate(), OnApplyChanges();
+	BOOL OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+private:
+	bool m_fAttached;
+	void AttachControls(), DetachControls();
+
+	CMap<UINT, UINT&, CWnd*, CWnd*> m_controls;
+
+protected:
+	void BindControl(UINT id, CWnd& control);
+};
+
+[uuid("60765CF5-01C2-4ee7-A44B-C791CF25FEA0")]
+class CDVSMainPPage : public CDVSBasePPage
+{
+	void FreeLangs(), AllocLangs(int nLangs);
+
+	WCHAR m_fn[MAX_PATH];
+	int m_iSelectedLanguage, m_nLangs;
+	WCHAR** m_ppLangs;
+	bool m_fOverridePlacement;
+	int	m_PlacementXperc, m_PlacementYperc;
+	STSStyle m_defStyle;
+	bool m_fOnlyShowForcedVobSubs;
+
+	CEdit m_fnedit;
+	CComboBox m_langs;
+	CButton m_oplacement;
+	CSpinButtonCtrl m_subposx, m_subposy;
+	CButton m_font, m_forcedsubs;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSMainPPage(LPUNKNOWN lpunk, HRESULT* phr);
+	virtual ~CDVSMainPPage();
+};
+
+[uuid("0180E49C-13BF-46db-9AFD-9F52292E1C22")]
+class CDVSGeneralPPage : public CDVSBasePPage
+{
+	int m_HorExt, m_VerExt, m_ResX2, m_ResX2minw, m_ResX2minh;
+	int m_LoadLevel;
+	bool m_fExternalLoad, m_fWebLoad, m_fEmbeddedLoad;
+
+	CComboBox m_verext;
+	CButton m_mod32fix;
+	CComboBox m_resx2;
+	CSpinButtonCtrl m_resx2w, m_resx2h;
+	CComboBox m_load;
+	CButton m_extload, m_webload, m_embload;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSGeneralPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("A8B25C0E-0894-4531-B668-AB1599FAF7F6")]
+class CDVSMiscPPage : public CDVSBasePPage
+{
+	bool m_fFlipPicture, m_fFlipSubtitles, m_fHideSubtitles, m_fOSD, m_fDoPreBuffering, m_fReloaderDisabled, m_fSaveFullPath;
+
+	CButton m_flippic, m_flipsub, m_hidesub, m_showosd, m_prebuff, m_autoreload, m_savefullpath, m_instupd;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSMiscPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("ACE4747B-35BD-4e97-9DD7-1D4245B0695C")]
+class CDVSTimingPPage : public CDVSBasePPage
+{
+	int m_SubtitleSpeedMul, m_SubtitleSpeedDiv, m_SubtitleDelay;
+	bool m_fMediaFPSEnabled;
+	double m_MediaFPS;
+
+	CButton m_modfps;
+	CEdit m_fps;
+	CSpinButtonCtrl m_subdelay, m_subspeedmul, m_subspeeddiv;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSTimingPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("F544E0F5-CA3C-47ea-A64D-35FCF1602396")]
+class CDVSAboutPPage : public CDVSBasePPage
+{
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+public:
+    CDVSAboutPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("525F116F-04AD-40a2-AE2F-A0C4E1AFEF98")]
+class CDVSZoomPPage : public CDVSBasePPage
+{
+	NORMALIZEDRECT m_rect;
+
+	CSpinButtonCtrl m_posx, m_posy, m_scalex, m_scaley;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSZoomPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("C2D6D98F-09CA-4524-AF64-1049B5665C9C")]
+class CDVSColorPPage : public CDVSBasePPage
+{
+	CListBox m_preflist, m_dynchglist;
+	CButton m_forcergb;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSColorPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
+
+[uuid("CE77C59C-CFD2-429f-868C-8B04D23F94CA")]
+class CDVSPathsPPage : public CDVSBasePPage
+{
+	CStringArray m_paths;
+
+	CListBox m_pathlist;
+	CEdit m_path;
+	CButton m_browse, m_remove, m_add;
+
+protected:
+    virtual bool OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
+	virtual void UpdateControlData(bool fSave);
+	virtual void UpdateObjectData(bool fSave);
+
+public:
+    CDVSPathsPPage(LPUNKNOWN lpunk, HRESULT* phr);
+};
diff --git a/vsfilter/IDirectVobSub.h b/vsfilter/IDirectVobSub.h
new file mode 100644
index 0000000000000000000000000000000000000000..a32f48e245bbded7c1337fbdff521db62447d1fe
--- /dev/null
+++ b/vsfilter/IDirectVobSub.h
@@ -0,0 +1,291 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "subtitles/STS.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    [uuid("EBE1FB08-3957-47ca-AF13-5827E5442E56")]
+	interface IDirectVobSub : public IUnknown 
+    {
+        STDMETHOD(get_FileName) (THIS_
+                    WCHAR* fn	// fn should point to a buffer allocated to at least the length of MAX_PATH (=260)
+                 ) PURE;
+
+        STDMETHOD(put_FileName) (THIS_
+                    WCHAR* fn
+                 ) PURE;
+
+		STDMETHOD(get_LanguageCount) (THIS_
+					int* nLangs
+                 ) PURE;
+
+		STDMETHOD(get_LanguageName) (THIS_
+					int iLanguage, 
+					WCHAR** ppName	// the returned *ppName is allocated with CoTaskMemAlloc
+                 ) PURE;
+
+		STDMETHOD(get_SelectedLanguage) (THIS_
+					int* iSelected
+                 ) PURE;
+
+        STDMETHOD(put_SelectedLanguage) (THIS_
+					int iSelected
+                 ) PURE;
+
+        STDMETHOD(get_HideSubtitles) (THIS_
+                    bool* fHideSubtitles
+                 ) PURE;
+
+        STDMETHOD(put_HideSubtitles) (THIS_
+                    bool fHideSubtitles
+                 ) PURE;
+
+        STDMETHOD(get_PreBuffering) (THIS_
+					bool* fDoPreBuffering
+                 ) PURE;
+
+        STDMETHOD(put_PreBuffering) (THIS_
+					bool fDoPreBuffering
+                 ) PURE;
+
+        STDMETHOD(get_Placement) (THIS_
+					bool* fOverridePlacement,
+					int* xperc,
+					int* yperc
+                 ) PURE;
+
+        STDMETHOD(put_Placement) (THIS_
+					bool fOverridePlacement,
+					int xperc,
+					int yperc
+                 ) PURE;
+
+        STDMETHOD(get_VobSubSettings) (THIS_
+					bool* fBuffer,
+					bool* fOnlyShowForcedSubs,
+					bool* fPolygonize
+                 ) PURE;
+
+        STDMETHOD(put_VobSubSettings) (THIS_
+					bool fBuffer,
+					bool fOnlyShowForcedSubs,
+					bool fPolygonize
+                 ) PURE;
+
+        STDMETHOD(get_TextSettings) (THIS_
+					void* lf,
+					int lflen, // depending on lflen, lf must point to LOGFONTA or LOGFONTW
+					COLORREF* color,
+					bool* fShadow,
+					bool* fOutline,
+					bool* fAdvancedRenderer
+                 ) PURE;
+
+        STDMETHOD(put_TextSettings) (THIS_
+					void* lf,
+					int lflen,
+					COLORREF color,
+					bool fShadow,
+					bool fOutline,
+					bool fAdvancedRenderer
+                 ) PURE;
+
+        STDMETHOD(get_Flip) (THIS_
+                    bool* fPicture,
+                    bool* fSubtitles
+                 ) PURE;
+
+        STDMETHOD(put_Flip) (THIS_
+                    bool fPicture,
+                    bool fSubtitles
+                 ) PURE;
+
+        STDMETHOD(get_OSD) (THIS_
+					bool* fOSD
+                 ) PURE;
+
+        STDMETHOD(put_OSD) (THIS_
+					bool fOSD
+                 ) PURE;
+
+        STDMETHOD(get_SaveFullPath) (THIS_
+					bool* fSaveFullPath
+                 ) PURE;
+
+        STDMETHOD(put_SaveFullPath) (THIS_
+					bool fSaveFullPath
+                 ) PURE;
+
+        STDMETHOD(get_SubtitleTiming) (THIS_
+					int* delay,
+					int* speedmul,
+					int* speeddiv
+                 ) PURE;
+
+        STDMETHOD(put_SubtitleTiming) (THIS_
+					int delay,
+					int speedmul,
+					int speeddiv
+                 ) PURE;
+
+        STDMETHOD(get_MediaFPS) (THIS_
+					bool* fEnabled,
+					double* fps
+                 ) PURE;
+
+        STDMETHOD(put_MediaFPS) (THIS_
+					bool fEnabled,
+					double fps
+                 ) PURE;
+
+		// no longer supported
+
+        STDMETHOD(get_ColorFormat) (THIS_
+					int* iPosition
+                 ) PURE;
+
+        STDMETHOD(put_ColorFormat) (THIS_
+					int iPosition
+                 ) PURE;
+
+		//
+
+        STDMETHOD(get_ZoomRect) (THIS_
+					NORMALIZEDRECT* rect
+                 ) PURE;
+
+        STDMETHOD(put_ZoomRect) (THIS_
+					NORMALIZEDRECT* rect
+                 ) PURE;
+
+		//
+
+        STDMETHOD(UpdateRegistry) (THIS_
+                 ) PURE;
+
+		//
+
+		STDMETHOD(HasConfigDialog) (THIS_
+					int iSelected
+				) PURE;
+
+		STDMETHOD(ShowConfigDialog) (THIS_	// if available, this will popup a child dialog allowing the user to edit the style options
+					int iSelected, 
+					HWND hWndParent
+				) PURE; 
+
+		//
+
+        STDMETHOD(IsSubtitleReloaderLocked) (THIS_
+					bool* fLocked
+                 ) PURE;
+
+        STDMETHOD(LockSubtitleReloader) (THIS_
+					bool fLock
+                 ) PURE;
+
+		STDMETHOD(get_SubtitleReloader) (THIS_
+					bool* fDisabled
+                 ) PURE;
+
+        STDMETHOD(put_SubtitleReloader) (THIS_
+					bool fDisable
+                 ) PURE;
+
+		//
+
+        STDMETHOD(get_ExtendPicture) (THIS_
+					int* horizontal, // 0 - disabled, 1 - mod32 extension (width = (width+31)&~31)
+					int* vertical, // 0 - disabled, 1 - 16:9, 2 - 4:3, 0x80 - crop (use crop together with 16:9 or 4:3, eg 0x81 will crop to 16:9 if the picture was taller)
+					int* resx2, // 0 - disabled, 1 - enabled, 2 - depends on the original resolution
+					int* resx2minw, // resolution doubler will be used if width*height <= resx2minw*resx2minh (resx2minw*resx2minh equals to 384*288 by default)
+					int* resx2minh 
+                 ) PURE;
+
+        STDMETHOD(put_ExtendPicture) (THIS_
+					int horizontal,
+					int vertical,
+					int resx2,
+					int resx2minw,
+					int resx2minh
+                 ) PURE;
+
+        STDMETHOD(get_LoadSettings) (THIS_
+					int* level, // 0 - when needed, 1 - always, 2 - disabled
+					bool* fExternalLoad,
+					bool* fWebLoad, 
+					bool* fEmbeddedLoad
+                 ) PURE;
+
+        STDMETHOD(put_LoadSettings) (THIS_
+					int level,
+					bool fExternalLoad,
+					bool fWebLoad, 
+					bool fEmbeddedLoad
+				) PURE;
+	};
+
+    [uuid("FE6EC6A0-21CA-4970-9EF0-B296F7F38AF0")]
+	interface ISubClock : public IUnknown
+	{
+        STDMETHOD(SetTime)(REFERENCE_TIME rt) PURE;
+        STDMETHOD_(REFERENCE_TIME, GetTime)() PURE;
+	};
+
+    [uuid("0665B760-FBC1-46C3-A35F-E471527C96A4")]
+	interface ISubClock2 : public ISubClock
+	{
+        STDMETHOD(SetAvgTimePerFrame)(REFERENCE_TIME rt) PURE;
+        STDMETHOD(GetAvgTimePerFrame)(REFERENCE_TIME* prt) PURE; // return S_OK only if *prt was set and is valid
+	};
+
+    [uuid("AB52FC9C-2415-4dca-BC1C-8DCC2EAE8150")]
+	interface IDirectVobSub2 : public IDirectVobSub
+	{
+        STDMETHOD(AdviseSubClock) (THIS_
+                    ISubClock* pSubClock
+				) PURE;
+
+		STDMETHOD_(bool, get_Forced) (THIS_
+				) PURE;
+
+        STDMETHOD(put_Forced) (THIS_
+                    bool fForced
+				) PURE;
+
+		STDMETHOD(get_TextSettings) (THIS_
+					STSStyle* pDefStyle
+				) PURE;
+
+		STDMETHOD(put_TextSettings) (THIS_
+					STSStyle* pDefStyle
+				) PURE;
+	};
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/vsfilter/StdAfx.cpp b/vsfilter/StdAfx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..26737adb3b20cfba90a78e82bc9f58fc51bf9bbf
--- /dev/null
+++ b/vsfilter/StdAfx.cpp
@@ -0,0 +1,26 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+//  stdafx.pch will be the pre-compiled header
+//  stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
diff --git a/vsfilter/StdAfx.h b/vsfilter/StdAfx.h
new file mode 100644
index 0000000000000000000000000000000000000000..129f4c41c680b6db650768835b652bb5c401ee45
--- /dev/null
+++ b/vsfilter/StdAfx.h
@@ -0,0 +1,57 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+//      or project specific include files that are used frequently,
+//      but are changed infrequently
+
+#if !defined(AFX_STDAFX_H__E8AD1C3F_323D_4812_A1DE_AA2419CE2AF3__INCLUDED_)
+#define AFX_STDAFX_H__E8AD1C3F_323D_4812_A1DE_AA2419CE2AF3__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define STRICT
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0400
+#endif
+#define _ATL_APARTMENT_THREADED
+
+#include <afxwin.h>
+#include <afxdisp.h>
+
+#include <atlbase.h>
+//You may derive a class from CComModule and use it if you want to override
+//something, but do not change the name of _Module
+extern CComModule _Module;
+#include <atlcom.h>
+
+#define AFX_CLASS AFX_CLASS_IMPORT
+
+#include <streams.h>
+#include <dvdmedia.h>
+#include "dsutil/DSUtil.h"
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__E8AD1C3F_323D_4812_A1DE_AA2419CE2AF3__INCLUDED)
diff --git a/vsfilter/StyleEditorDialog.cpp b/vsfilter/StyleEditorDialog.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b931340ab9704e890b83d2ffee63f6eeecc470b1
--- /dev/null
+++ b/vsfilter/StyleEditorDialog.cpp
@@ -0,0 +1,282 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// StyleEditorDialog.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include <math.h>
+#include <afxdlgs.h>
+#include "StyleEditorDialog.h"
+
+IMPLEMENT_DYNAMIC(CColorStatic, CStatic)
+
+BEGIN_MESSAGE_MAP(CColorStatic, CStatic)
+END_MESSAGE_MAP()
+
+
+// CStyleEditorDialog dialog
+
+IMPLEMENT_DYNAMIC(CStyleEditorDialog, CDialog)
+CStyleEditorDialog::CStyleEditorDialog(CString title, STSStyle* pstss, CWnd* pParent /*=NULL*/)
+	: CDialog(CStyleEditorDialog::IDD, pParent)
+	, m_title(title)
+	, m_stss(*pstss)
+	, m_pParent(pParent)
+	, m_iCharset(0)
+	, m_spacing(0)
+	, m_angle(0)
+	, m_scalex(0)
+	, m_scaley(0)
+	, m_borderstyle(0)
+	, m_borderwidth(0)
+	, m_shadowdepth(0)
+	, m_screenalignment(0)
+	, m_margin(0,0,0,0)
+	, m_linkalphasliders(FALSE)
+{
+}
+
+CStyleEditorDialog::~CStyleEditorDialog()
+{
+}
+
+void CStyleEditorDialog::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_BUTTON1, m_font);
+	DDX_CBIndex(pDX, IDC_COMBO1, m_iCharset);
+	DDX_Control(pDX, IDC_COMBO1, m_charset);
+	DDX_Text(pDX, IDC_EDIT3, m_spacing);
+	DDX_Control(pDX, IDC_SPIN3, m_spacingspin);
+	DDX_Text(pDX, IDC_EDIT11, m_angle);
+	DDX_Control(pDX, IDC_SPIN10, m_anglespin);
+	DDX_Text(pDX, IDC_EDIT5, m_scalex);
+	DDX_Control(pDX, IDC_SPIN4, m_scalexspin);
+	DDX_Text(pDX, IDC_EDIT6, m_scaley);
+	DDX_Control(pDX, IDC_SPIN5, m_scaleyspin);
+	DDX_Radio(pDX, IDC_RADIO1, m_borderstyle);
+	DDX_Text(pDX, IDC_EDIT1, m_borderwidth);
+	DDX_Control(pDX, IDC_SPIN1, m_borderwidthspin);
+	DDX_Text(pDX, IDC_EDIT2, m_shadowdepth);
+	DDX_Control(pDX, IDC_SPIN2, m_shadowdepthspin);
+	DDX_Radio(pDX, IDC_RADIO3, m_screenalignment);
+	DDX_Text(pDX, IDC_EDIT7, m_margin.left);
+	DDX_Control(pDX, IDC_SPIN6, m_marginleftspin);
+	DDX_Text(pDX, IDC_EDIT8, m_margin.right);
+	DDX_Control(pDX, IDC_SPIN7, m_marginrightspin);
+	DDX_Text(pDX, IDC_EDIT9, m_margin.top);
+	DDX_Control(pDX, IDC_SPIN8, m_margintopspin);
+	DDX_Text(pDX, IDC_EDIT10, m_margin.bottom);
+	DDX_Control(pDX, IDC_SPIN9, m_marginbottomspin);
+	DDX_Control(pDX, IDC_COLORPRI, m_color[0]);
+	DDX_Control(pDX, IDC_COLORSEC, m_color[1]);
+	DDX_Control(pDX, IDC_COLOROUTL, m_color[2]);
+	DDX_Control(pDX, IDC_COLORSHAD, m_color[3]);
+	DDX_Slider(pDX, IDC_SLIDER2, m_alpha[0]);
+	DDX_Slider(pDX, IDC_SLIDER3, m_alpha[1]);
+	DDX_Slider(pDX, IDC_SLIDER5, m_alpha[2]);
+	DDX_Slider(pDX, IDC_SLIDER6, m_alpha[3]);
+	DDX_Control(pDX, IDC_SLIDER2, m_alphasliders[0]);
+	DDX_Control(pDX, IDC_SLIDER3, m_alphasliders[1]);
+	DDX_Control(pDX, IDC_SLIDER5, m_alphasliders[2]);
+	DDX_Control(pDX, IDC_SLIDER6, m_alphasliders[3]);
+	DDX_Check(pDX, IDC_CHECK1, m_linkalphasliders);
+}
+
+void CStyleEditorDialog::UpdateControlData(bool fSave)
+{
+	if(fSave)
+	{
+		UpdateData();
+
+		if(m_iCharset >= 0) m_stss.charSet = m_charset.GetItemData(m_iCharset);
+		m_stss.fontSpacing = m_spacing;
+		m_stss.fontAngleZ = m_angle;
+		m_stss.fontScaleX = m_scalex;
+		m_stss.fontScaleY = m_scaley;
+
+		m_stss.borderStyle = m_borderstyle;
+		m_stss.outlineWidth = m_borderwidth;
+		m_stss.shadowDepth = m_shadowdepth;
+
+		m_stss.scrAlignment = m_screenalignment+1;
+		m_stss.marginRect = m_margin;
+
+		for(int i = 0; i < 4; i++) m_stss.alpha[i] = 255-m_alpha[i];
+	}
+	else
+	{
+		m_font.SetWindowText(m_stss.fontName);
+		m_iCharset = -1;
+		for(int i = 0; i < CharSetLen; i++)
+		{
+			CString str;
+			str.Format(_T("%s (%d)"), CharSetNames[i], CharSetList[i]);
+			m_charset.AddString(str);
+			m_charset.SetItemData(i, CharSetList[i]);
+			if(m_stss.charSet == CharSetList[i]) m_iCharset = i;
+		}
+		// TODO: allow floats in these edit boxes
+		m_spacing = m_stss.fontSpacing;
+		m_spacingspin.SetRange32(-10000, 10000);
+		while(m_stss.fontAngleZ < 0) m_stss.fontAngleZ += 360;
+		m_angle = fmod(m_stss.fontAngleZ, 360);
+		m_anglespin.SetRange32(0, 359);
+		m_scalex = m_stss.fontScaleX;
+		m_scalexspin.SetRange32(-10000, 10000);
+		m_scaley = m_stss.fontScaleY;
+		m_scaleyspin.SetRange32(-10000, 10000);
+
+		m_borderstyle = m_stss.borderStyle;
+		m_borderwidth = m_stss.outlineWidth;
+		m_borderwidthspin.SetRange32(0, 10000);
+		m_shadowdepth = m_stss.shadowDepth;
+		m_shadowdepthspin.SetRange32(0, 10000);
+
+		m_screenalignment = m_stss.scrAlignment-1;
+		m_margin = m_stss.marginRect;
+		m_marginleftspin.SetRange32(-10000, 10000);
+		m_marginrightspin.SetRange32(-10000, 10000);
+		m_margintopspin.SetRange32(-10000, 10000);
+		m_marginbottomspin.SetRange32(-10000, 10000);
+
+		for(int i = 0; i < 4; i++)
+		{
+			m_color[i].SetColorPtr(&m_stss.colors[i]);
+			m_alpha[i] = 255-m_stss.alpha[i];
+			m_alphasliders[i].SetRange(0, 255);
+		}
+		
+		m_linkalphasliders = FALSE;
+
+		UpdateData(FALSE);
+	}
+}
+
+void CStyleEditorDialog::AskColor(int i)
+{
+	CColorDialog dlg(m_stss.colors[i]);
+	dlg.m_cc.Flags |= CC_FULLOPEN;
+	if(dlg.DoModal() == IDOK)
+	{
+		m_stss.colors[i] = dlg.m_cc.rgbResult;
+		m_color[i].Invalidate();
+	}
+}
+
+BEGIN_MESSAGE_MAP(CStyleEditorDialog, CDialog)
+	ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedButton1)
+	ON_STN_CLICKED(IDC_COLORPRI, OnStnClickedColorpri)
+	ON_STN_CLICKED(IDC_COLORSEC, OnStnClickedColorsec)
+	ON_STN_CLICKED(IDC_COLOROUTL, OnStnClickedColoroutl)
+	ON_STN_CLICKED(IDC_COLORSHAD, OnStnClickedColorshad)
+	ON_BN_CLICKED(IDC_CHECK1, OnBnClickedCheck1)
+	ON_WM_HSCROLL()
+END_MESSAGE_MAP()
+
+
+// CStyleEditorDialog message handlers
+
+BOOL CStyleEditorDialog::OnInitDialog()
+{
+	CDialog::OnInitDialog();
+
+	SetWindowText(_T("Style Editor - \"") + m_title + _T("\""));
+
+	UpdateControlData(false);
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	// EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CStyleEditorDialog::OnOK()
+{
+	UpdateControlData(true);
+
+	CDialog::OnOK();
+}
+
+void CStyleEditorDialog::OnBnClickedButton1()
+{
+	LOGFONT lf;
+	lf <<= m_stss;
+
+	CFontDialog dlg(&lf, CF_SCREENFONTS|CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCALABLEONLY|CF_EFFECTS);
+	if(dlg.DoModal() == IDOK)
+	{
+		CString str(lf.lfFaceName);
+		if(str.GetLength() > 16) str = str.Left(14) + _T("...");
+		m_font.SetWindowText(str);
+
+		for(int i = 0, j = m_charset.GetCount(); i < j; i++)
+		{
+			if(m_charset.GetItemData(i) == lf.lfCharSet)
+			{
+				m_charset.SetCurSel(i);
+				break;
+			}
+		}
+
+		m_stss = lf;
+	}
+}
+
+void CStyleEditorDialog::OnStnClickedColorpri()
+{
+	AskColor(0);
+}
+
+void CStyleEditorDialog::OnStnClickedColorsec()
+{
+	AskColor(1);
+}
+
+void CStyleEditorDialog::OnStnClickedColoroutl()
+{
+	AskColor(2);
+}
+
+void CStyleEditorDialog::OnStnClickedColorshad()
+{
+	AskColor(3);
+}
+
+void CStyleEditorDialog::OnBnClickedCheck1()
+{
+	UpdateData();
+
+	int avg = 0;
+	for(int i = 0; i < 4; i++) avg += m_alphasliders[i].GetPos();
+	avg /= 4;
+	for(int i = 0; i < 4; i++) m_alphasliders[i].SetPos(avg);
+}
+
+void CStyleEditorDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
+{
+	if(m_linkalphasliders && pScrollBar)
+	{
+		int pos = ((CSliderCtrl*)pScrollBar)->GetPos();
+		for(int i = 0; i < 4; i++) m_alphasliders[i].SetPos(pos);
+	}
+
+	CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
+}
diff --git a/vsfilter/StyleEditorDialog.h b/vsfilter/StyleEditorDialog.h
new file mode 100644
index 0000000000000000000000000000000000000000..f02e41a07c34ca6b74fb000048131c498a188cd3
--- /dev/null
+++ b/vsfilter/StyleEditorDialog.h
@@ -0,0 +1,116 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+#include "resource.h"
+#include "afxwin.h"
+#include "afxcmn.h"
+#include "subtitles/STS.h"
+
+// CColorStatic dialog
+
+class CColorStatic : public CStatic
+{
+	DECLARE_DYNAMIC(CColorStatic)
+
+	COLORREF* m_pColor;
+
+public:
+	CColorStatic(CWnd* pParent = NULL) : m_pColor(NULL) {}
+	virtual ~CColorStatic() {}
+
+	void SetColorPtr(COLORREF* pColor) {m_pColor = pColor;}
+
+	DECLARE_MESSAGE_MAP()
+
+protected:
+	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
+	{
+		CRect r;
+		GetClientRect(r);
+		CDC::FromHandle(lpDrawItemStruct->hDC)->FillSolidRect(r, m_pColor ? *m_pColor : ::GetSysColor(COLOR_BTNFACE));
+	}
+};
+
+// CStyleEditorDialog dialog
+
+class CStyleEditorDialog : public CDialog
+{
+	DECLARE_DYNAMIC(CStyleEditorDialog)
+
+	CString m_title;
+	CWnd* m_pParent;
+
+	void UpdateControlData(bool fSave);
+	void AskColor(int i);
+
+public:
+	CStyleEditorDialog(CString title, STSStyle* pstss, CWnd* pParent = NULL);   // standard constructor
+	virtual ~CStyleEditorDialog();
+
+// Dialog Data
+	enum { IDD = IDD_STYLEDIALOG };
+
+	STSStyle m_stss;
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	virtual BOOL OnInitDialog();
+	virtual void OnOK();
+
+	DECLARE_MESSAGE_MAP()
+
+public:
+	CButton m_font;
+	int m_iCharset;
+	CComboBox m_charset;
+	int m_spacing;
+	CSpinButtonCtrl m_spacingspin;
+	int m_angle;
+	CSpinButtonCtrl m_anglespin;
+	int m_scalex;
+	CSpinButtonCtrl m_scalexspin;
+	int m_scaley;
+	CSpinButtonCtrl m_scaleyspin;
+	int m_borderstyle;
+	int m_borderwidth;
+	CSpinButtonCtrl m_borderwidthspin;
+	int m_shadowdepth;
+	CSpinButtonCtrl m_shadowdepthspin;
+	int m_screenalignment;
+	CRect m_margin;
+	CSpinButtonCtrl m_marginleftspin;
+	CSpinButtonCtrl m_marginrightspin;
+	CSpinButtonCtrl m_margintopspin;
+	CSpinButtonCtrl m_marginbottomspin;
+	CColorStatic m_color[4];
+	int m_alpha[4];
+	CSliderCtrl m_alphasliders[4];
+	BOOL m_linkalphasliders;
+
+	afx_msg void OnBnClickedButton1();
+	afx_msg void OnStnClickedColorpri();
+	afx_msg void OnStnClickedColorsec();
+	afx_msg void OnStnClickedColoroutl();
+	afx_msg void OnStnClickedColorshad();
+	afx_msg void OnBnClickedCheck1();
+	afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
+};
diff --git a/vsfilter/Systray.cpp b/vsfilter/Systray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..127ada181f390bd9439b07fd7bcbdea23188734d
--- /dev/null
+++ b/vsfilter/Systray.cpp
@@ -0,0 +1,421 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "resource.h"
+#include "DirectVobSubFilter.h"
+#include "DSUtil/DSUtil.h"
+
+// hWnd == INVALID_HANDLE_VALUE - get name, hWnd != INVALID_HANDLE_VALUE - show ppage
+static TCHAR* CallPPage(IFilterGraph* pGraph, int idx, HWND hWnd);
+
+static HHOOK g_hHook = (HHOOK)INVALID_HANDLE_VALUE;
+
+static UINT WM_DVSPREVSUB = RegisterWindowMessage(TEXT("WM_DVSPREVSUB"));
+static UINT WM_DVSNEXTSUB = RegisterWindowMessage(TEXT("WM_DVSNEXTSUB"));
+static UINT WM_DVSHIDESUB = RegisterWindowMessage(TEXT("WM_DVSHIDESUB"));
+static UINT WM_DVSSHOWSUB = RegisterWindowMessage(TEXT("WM_DVSSHOWSUB"));
+static UINT WM_DVSSHOWHIDESUB = RegisterWindowMessage(TEXT("WM_DVSSHOWHIDESUB"));
+static UINT s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
+static UINT WM_NOTIFYICON = RegisterWindowMessage(TEXT("MYWM_NOTIFYICON"));
+
+LRESULT CALLBACK HookProc(UINT code, WPARAM wParam, LPARAM lParam)
+{
+	MSG* msg = (MSG*)lParam;
+
+	if(msg->message == WM_KEYDOWN)
+	{
+		switch(msg->wParam)
+		{
+		case VK_F13: PostMessage(HWND_BROADCAST, WM_DVSPREVSUB, 0, 0); break;
+		case VK_F14: PostMessage(HWND_BROADCAST, WM_DVSNEXTSUB, 0, 0); break;
+		case VK_F15: PostMessage(HWND_BROADCAST, WM_DVSHIDESUB, 0, 0); break;
+		case VK_F16: PostMessage(HWND_BROADCAST, WM_DVSSHOWSUB, 0, 0); break;
+		case VK_F17: PostMessage(HWND_BROADCAST, WM_DVSSHOWHIDESUB, 0, 0); break;
+		default: break;
+		}
+	}
+
+	// Always call next hook in chain 
+	return CallNextHookEx(g_hHook, code,  wParam, lParam);
+} 
+
+class CSystrayWindow : public CWnd
+{
+	SystrayIconData* m_tbid;
+
+	void StepSub(int dir)
+	{
+		int iSelected, nLangs;
+		if(FAILED(m_tbid->dvs->get_LanguageCount(&nLangs))) return;
+		if(FAILED(m_tbid->dvs->get_SelectedLanguage(&iSelected))) return;
+        if(nLangs > 0) m_tbid->dvs->put_SelectedLanguage((iSelected+dir+nLangs)%nLangs);
+	}
+
+	void ShowSub(bool fShow)
+	{
+		m_tbid->dvs->put_HideSubtitles(!fShow);
+	}
+
+	void ToggleSub()
+	{
+		bool fShow;
+		if(FAILED(m_tbid->dvs->get_HideSubtitles(&fShow))) return;
+		m_tbid->dvs->put_HideSubtitles(!fShow);
+	}
+
+public:
+	CSystrayWindow(SystrayIconData* tbid) : m_tbid(tbid) {}
+
+protected:
+	DECLARE_MESSAGE_MAP()
+
+public:
+	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+	afx_msg void OnClose();
+	afx_msg void OnDestroy();
+	afx_msg void OnTimer(UINT nIDEvent);
+	afx_msg LRESULT OnDVSPrevSub(WPARAM, LPARAM);
+	afx_msg LRESULT OnDVSNextSub(WPARAM, LPARAM);
+	afx_msg LRESULT OnDVSHideSub(WPARAM, LPARAM);
+	afx_msg LRESULT OnDVSShowSub(WPARAM, LPARAM);
+	afx_msg LRESULT OnDVSShowHideSub(WPARAM, LPARAM);
+	afx_msg LRESULT OnTaskBarRestart(WPARAM, LPARAM);
+	afx_msg LRESULT OnNotifyIcon(WPARAM, LPARAM);
+};
+
+BEGIN_MESSAGE_MAP(CSystrayWindow, CWnd)
+	ON_WM_CREATE()
+	ON_WM_CLOSE()
+	ON_WM_DESTROY()
+	ON_WM_TIMER()
+	ON_REGISTERED_MESSAGE(WM_DVSPREVSUB, OnDVSPrevSub)
+	ON_REGISTERED_MESSAGE(WM_DVSNEXTSUB, OnDVSNextSub)
+	ON_REGISTERED_MESSAGE(WM_DVSHIDESUB, OnDVSHideSub)
+	ON_REGISTERED_MESSAGE(WM_DVSSHOWSUB, OnDVSShowSub)
+	ON_REGISTERED_MESSAGE(WM_DVSSHOWHIDESUB, OnDVSShowHideSub)
+	ON_REGISTERED_MESSAGE(s_uTaskbarRestart, OnTaskBarRestart)
+	ON_REGISTERED_MESSAGE(WM_NOTIFYICON, OnNotifyIcon)
+END_MESSAGE_MAP()
+
+int CSystrayWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+	if(CWnd::OnCreate(lpCreateStruct) == -1)
+		return -1;
+
+	if(g_hHook == INVALID_HANDLE_VALUE)
+	{
+		AFX_MANAGE_STATE(AfxGetStaticModuleState());
+//		g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)HookProc, AfxGetInstanceHandle(), 0);
+	}
+
+	SetTimer(1, 5000, NULL);
+
+	PostMessage(s_uTaskbarRestart);
+
+	return 0;
+}
+
+void CSystrayWindow::OnClose()
+{
+	DestroyWindow();
+}
+
+void CSystrayWindow::OnDestroy()
+{
+	NOTIFYICONDATA tnid; 
+	tnid.cbSize = sizeof(NOTIFYICONDATA); 
+	tnid.hWnd = m_hWnd;
+	tnid.uID = IDI_ICON1; 
+	Shell_NotifyIcon(NIM_DELETE, &tnid); 
+
+	if(g_hHook != INVALID_HANDLE_VALUE)
+	{
+		UnhookWindowsHookEx(g_hHook);
+		g_hHook = (HHOOK)INVALID_HANDLE_VALUE;
+	}
+
+	PostQuitMessage(0); 
+}
+
+void CSystrayWindow::OnTimer(UINT nIDEvent)
+{
+	if(nIDEvent == 1)
+	{
+		UINT fScreenSaver = 0;
+		if(SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (PVOID)&fScreenSaver, 0))
+		{
+			SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, 0, 0, SPIF_SENDWININICHANGE); // this might not be needed at all...
+			SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, fScreenSaver, 0, SPIF_SENDWININICHANGE);
+		}
+	}
+
+	CWnd::OnTimer(nIDEvent);
+}
+
+LRESULT CSystrayWindow::OnDVSPrevSub(WPARAM, LPARAM)
+	{StepSub(-1); return 0;}
+LRESULT CSystrayWindow::OnDVSNextSub(WPARAM, LPARAM)
+	{StepSub(+1); return 0;}
+LRESULT CSystrayWindow::OnDVSHideSub(WPARAM, LPARAM)
+	{ShowSub(false); return 0;}
+LRESULT CSystrayWindow::OnDVSShowSub(WPARAM, LPARAM)
+	{ShowSub(true); return 0;}
+LRESULT CSystrayWindow::OnDVSShowHideSub(WPARAM, LPARAM)
+	{ToggleSub(); return 0;}
+
+LRESULT CSystrayWindow::OnTaskBarRestart(WPARAM, LPARAM)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	if(m_tbid->fShowIcon)
+	{
+		NOTIFYICONDATA tnid; 
+		tnid.cbSize = sizeof(NOTIFYICONDATA); 
+		tnid.hWnd = m_hWnd; 
+		tnid.uID = IDI_ICON1; 
+		tnid.hIcon = (HICON)LoadIcon(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ICON1));
+//		tnid.hIcon = (HICON)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 0, 0, LR_LOADTRANSPARENT);
+		tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 
+		tnid.uCallbackMessage = WM_NOTIFYICON; 
+		lstrcpyn(tnid.szTip, TEXT("DirectVobSub"), sizeof(tnid.szTip)); 
+
+		BOOL res = Shell_NotifyIcon(NIM_ADD, &tnid); 
+
+		if(tnid.hIcon) DestroyIcon(tnid.hIcon); 
+
+		return res?0:-1;
+	}
+
+	return 0;
+}
+
+LRESULT CSystrayWindow::OnNotifyIcon(WPARAM wParam, LPARAM lParam)
+{
+    if((UINT)wParam != IDI_ICON1)
+		return -1;
+
+	HWND hWnd = m_hWnd;
+	
+	switch((UINT)lParam)
+	{
+		case WM_LBUTTONDBLCLK:
+		{
+			// IMPORTANT: we must not hold the graph at the same time as showing the property page 
+			// or else when closing the app the graph doesn't get released and dvobsub's JoinFilterGraph
+			// is never called to close us down.
+
+			CComPtr<IBaseFilter> pBF2;
+
+			BeginEnumFilters(m_tbid->graph, pEF, pBF)
+			{
+				if(!CComQIPtr<IDirectVobSub>(pBF))
+					continue;
+
+				if(CComQIPtr<IVideoWindow> pVW = m_tbid->graph) 
+				{
+					HWND hwnd;
+					if(SUCCEEDED(pVW->get_Owner((OAHWND*)&hwnd))
+					|| SUCCEEDED(pVW->get_MessageDrain((OAHWND*)&hwnd)))
+						hWnd = hwnd;
+				}
+
+				pBF2 = pBF;
+
+				break;
+			}
+			EndEnumFilters
+
+			if(pBF2)
+				ShowPPage(pBF2, hWnd);
+		}
+		break;
+
+		case WM_RBUTTONDOWN:
+		{
+			POINT p;
+			GetCursorPos(&p);
+
+			CInterfaceArray<IAMStreamSelect> pStreams;
+			CStringArray names;
+
+			BeginEnumFilters(m_tbid->graph, pEF, pBF)
+			{
+				CString name = GetFilterName(pBF);
+				if(name.IsEmpty()) continue;
+
+				if(CComQIPtr<IAMStreamSelect> pSS = pBF)
+				{
+					pStreams.Add(pSS);
+					names.Add(name);
+				}
+			}
+			EndEnumFilters
+
+			CMenu popup;
+			popup.CreatePopupMenu();
+
+			for(int j = 0; j < pStreams.GetCount(); j++)
+			{
+				bool fMMSwitcher = !names[j].Compare(_T("Morgan Stream Switcher"));
+
+				DWORD cStreams = 0;
+				pStreams[j]->Count(&cStreams);
+
+				DWORD flags, group, prevgroup = -1;
+				
+				for(UINT i = 0; i < cStreams; i++)
+				{
+					WCHAR* pName = NULL;
+
+					if(S_OK == pStreams[j]->Info(i, 0, &flags, 0, &group, &pName, 0, 0))
+					{
+						if(prevgroup != group && i > 1) 
+						{
+							if(fMMSwitcher) {cStreams = i; break;}
+							popup.AppendMenu(MF_SEPARATOR);
+						}
+						prevgroup = group;
+
+						if(pName)
+						{
+							popup.AppendMenu(MF_ENABLED|MF_STRING|(flags?MF_CHECKED:MF_UNCHECKED), (1<<15)|(j<<8)|(i), CString(pName));
+							CoTaskMemFree(pName);
+						}
+					}
+				}
+
+				if(cStreams > 0) popup.AppendMenu(MF_SEPARATOR);
+			}
+
+			int i;
+
+			TCHAR* str;
+			for(i = 0; str = CallPPage(m_tbid->graph, i, (HWND)INVALID_HANDLE_VALUE); i++)
+			{
+				if(_tcsncmp(str, _T("DivX MPEG"), 9) || m_tbid->fRunOnce) // divx3's ppage will crash if the graph hasn't been run at least once yet
+					popup.AppendMenu(MF_ENABLED|MF_STRING|MF_UNCHECKED, (1<<14)|(i), str);
+
+				delete [] str;
+			}
+
+			SetForegroundWindow();
+			UINT id = popup.TrackPopupMenu(TPM_LEFTBUTTON|TPM_RETURNCMD, p.x, p.y, CWnd::FromHandle(hWnd), 0);
+			PostMessage(WM_NULL);
+
+			if(id & (1<<15)) 
+			{
+				pStreams[(id>>8)&0x3f]->Enable(id&0xff, AMSTREAMSELECTENABLE_ENABLE);
+			}
+			else if(id & (1<<14))
+			{
+				if(CComQIPtr<IVideoWindow> pVW = m_tbid->graph)
+				{
+					HWND hwnd;
+					if(SUCCEEDED(pVW->get_Owner((OAHWND*)&hwnd))
+					|| SUCCEEDED(pVW->get_MessageDrain((OAHWND*)&hwnd)))
+						hWnd = hwnd;
+				}
+
+				CallPPage(m_tbid->graph, id&0xff, hWnd);
+			}
+		}
+		break; 
+
+		default: 
+			break; 
+	}
+
+	return 0;
+}
+
+//
+
+DWORD CALLBACK SystrayThreadProc(void* pParam)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CSystrayWindow wnd((SystrayIconData*)pParam);
+	if(!wnd.CreateEx(0, AfxRegisterWndClass(0), _T("DVSWND"), WS_OVERLAPPED, CRect(0, 0, 0, 0), NULL, 0, NULL))
+		return -1;
+
+	((SystrayIconData*)pParam)->hSystrayWnd = wnd.m_hWnd;
+
+	MSG msg;
+	while(GetMessage(&msg, NULL/*wnd.m_hWnd*/, 0, 0))
+	{
+		TranslateMessage(&msg);
+		DispatchMessage(&msg);
+	}
+
+	return 0;
+}
+
+// TODO: replace this function
+
+// hWnd == INVALID_HANDLE_VALUE - get name, hWnd != INVALID_HANDLE_VALUE - show ppage
+static TCHAR* CallPPage(IFilterGraph* pGraph, int idx, HWND hWnd)
+{
+	int i = 0;
+	bool fFound = false;
+
+	WCHAR* wstr = NULL;
+	CComPtr<IBaseFilter> pFilter;
+	CAUUID caGUID;
+	caGUID.pElems = NULL;
+
+	BeginEnumFilters(pGraph, pEF, pBF)
+	{
+		CComQIPtr<ISpecifyPropertyPages> pSPS = pBF;
+		if(!pSPS) continue;
+
+		if(i == idx)
+		{ 
+			pFilter = pBF;
+			pSPS->GetPages(&caGUID);
+			wstr = _wcsdup(CStringW(GetFilterName(pBF))); // double char-wchar conversion happens in the non-unicode build, but anyway... :)
+			break;
+		}
+
+		i++;
+	}
+	EndEnumFilters
+
+	TCHAR* ret = NULL;
+
+	if(pFilter)
+	{
+		if(hWnd != INVALID_HANDLE_VALUE)
+		{
+			ShowPPage(pFilter, hWnd);
+		}
+		else
+		{
+			if(ret = new TCHAR[wcslen(wstr)+1])
+				_tcscpy(ret, CString(wstr));
+		}
+	}
+
+	if(caGUID.pElems) CoTaskMemFree(caGUID.pElems);
+	if(wstr) free(wstr);
+
+	return(ret);
+}
diff --git a/vsfilter/Systray.h b/vsfilter/Systray.h
new file mode 100644
index 0000000000000000000000000000000000000000..f794c63baca7ffe0c32825ad7c929c786e457084
--- /dev/null
+++ b/vsfilter/Systray.h
@@ -0,0 +1,24 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+extern DWORD CALLBACK SystrayThreadProc(void* pParam);
diff --git a/vsfilter/TextInputPin.cpp b/vsfilter/TextInputPin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e344f268b77940e274f21a33dba3f3a2832eaff
--- /dev/null
+++ b/vsfilter/TextInputPin.cpp
@@ -0,0 +1,46 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "DirectVobSubFilter.h"
+#include "TextInputPin.h"
+#include "DSUtil/DSUtil.h"
+
+CTextInputPin::CTextInputPin(CDirectVobSubFilter* pFilter, CCritSec* pLock, CCritSec* pSubLock, HRESULT* phr)
+	: CSubtitleInputPin(pFilter, pLock, pSubLock, phr)
+	, m_pDVS(pFilter)
+{
+}
+
+void CTextInputPin::AddSubStream(ISubStream* pSubStream)
+{
+	m_pDVS->AddSubStream(pSubStream);
+}
+
+void CTextInputPin::RemoveSubStream(ISubStream* pSubStream)
+{
+	m_pDVS->RemoveSubStream(pSubStream);
+}
+
+void CTextInputPin::InvalidateSubtitle(REFERENCE_TIME rtStart, ISubStream* pSubStream)
+{
+	m_pDVS->InvalidateSubtitle(rtStart, (DWORD_PTR)(ISubStream*)pSubStream);
+}
diff --git a/vsfilter/TextInputPin.h b/vsfilter/TextInputPin.h
new file mode 100644
index 0000000000000000000000000000000000000000..99174a5a7c363463d3670a756f038ba93d64d7fe
--- /dev/null
+++ b/vsfilter/TextInputPin.h
@@ -0,0 +1,40 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "subtitles/SubtitleInputPin.h"
+
+class CDirectVobSubFilter;
+
+class CTextInputPin : public CSubtitleInputPin
+{
+    CDirectVobSubFilter* m_pDVS;
+
+protected:
+	void AddSubStream(ISubStream* pSubStream);
+	void RemoveSubStream(ISubStream* pSubStream);
+	void InvalidateSubtitle(REFERENCE_TIME rtStart, ISubStream* pSubStream);
+
+public:
+    CTextInputPin(CDirectVobSubFilter* pFilter, CCritSec* pLock, CCritSec* pSubLock, HRESULT* phr);
+};
+
diff --git a/vsfilter/VSFilter.cpp b/vsfilter/VSFilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c934f919907621a850e100fe7c3db776c2189790
--- /dev/null
+++ b/vsfilter/VSFilter.cpp
@@ -0,0 +1,178 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "DirectVobSubFilter.h"
+#include "DirectVobSubPropPage.h"
+#include "VSFilter.h"
+#include "DSUtil/MediaTypes.h"
+
+#include <initguid.h>
+#include "include/moreuuids.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// CVSFilterApp 
+
+BEGIN_MESSAGE_MAP(CVSFilterApp, CWinApp)
+END_MESSAGE_MAP()
+
+CVSFilterApp::CVSFilterApp()
+{
+}
+
+extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
+
+BOOL CVSFilterApp::InitInstance()
+{
+	if(!CWinApp::InitInstance())
+		return FALSE;
+
+	SetRegistryKey(_T("Gabest"));
+
+	DllEntryPoint(AfxGetInstanceHandle(), DLL_PROCESS_ATTACH, 0); // "DllMain" of the dshow baseclasses
+
+	STARTUPINFO si;
+	GetStartupInfo(&si);
+	m_AppName = CString(si.lpTitle);
+	m_AppName.Replace('\\', '/');
+	m_AppName = m_AppName.Mid(m_AppName.ReverseFind('/')+1);
+	m_AppName.MakeLower();
+
+	return TRUE;
+}
+
+int CVSFilterApp::ExitInstance()
+{
+	DllEntryPoint(AfxGetInstanceHandle(), DLL_PROCESS_DETACH, 0); // "DllMain" of the dshow baseclasses
+
+	return CWinApp::ExitInstance();
+}
+
+HINSTANCE CVSFilterApp::LoadAppLangResourceDLL()
+{
+	CString fn;
+	fn.ReleaseBufferSetLength(::GetModuleFileName(m_hInstance, fn.GetBuffer(MAX_PATH), MAX_PATH));
+	fn = fn.Mid(fn.ReverseFind('\\')+1);
+	fn = fn.Left(fn.ReverseFind('.')+1);
+	fn = fn + _T("lang");
+	return ::LoadLibrary(fn);
+}
+
+CVSFilterApp theApp;
+
+//////////////////////////////////////////////////////////////////////////
+
+const AMOVIESETUP_MEDIATYPE sudPinTypesIn[] =
+{
+	{&MEDIATYPE_NULL, &MEDIASUBTYPE_NULL},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_YUY2},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_YV12},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_I420},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_IYUV},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_RGB32},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_RGB565},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_RGB555},
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_RGB24},
+};
+
+const AMOVIESETUP_MEDIATYPE sudPinTypesIn2[] =
+{
+	{&MEDIATYPE_Text, &MEDIASUBTYPE_None},
+};
+
+const AMOVIESETUP_MEDIATYPE sudPinTypesOut[] =
+{
+	{&MEDIATYPE_Video, &MEDIASUBTYPE_None},
+};
+
+const AMOVIESETUP_PIN sudpPins[] =
+{
+    {L"Input", FALSE, FALSE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesIn), sudPinTypesIn},
+    {L"Output", FALSE, TRUE, FALSE, FALSE, &CLSID_NULL, NULL, countof(sudPinTypesOut), sudPinTypesOut},
+    {L"Input2", TRUE, FALSE, FALSE, TRUE, &CLSID_NULL, NULL, countof(sudPinTypesIn2), sudPinTypesIn2}
+};
+
+/*const*/ AMOVIESETUP_FILTER sudFilter[] =
+{
+	{&__uuidof(CDirectVobSubFilter), L"DirectVobSub", MERIT_DO_NOT_USE, countof(sudpPins), sudpPins},
+	{&__uuidof(CDirectVobSubFilter2), L"DirectVobSub (auto-loading version)", MERIT_PREFERRED+2, countof(sudpPins), sudpPins},
+};
+
+CFactoryTemplate g_Templates[] =
+{
+	{sudFilter[0].strName, sudFilter[0].clsID, CreateInstance<CDirectVobSubFilter>, NULL, &sudFilter[0]},
+    {sudFilter[1].strName, sudFilter[1].clsID, CreateInstance<CDirectVobSubFilter2>, NULL, &sudFilter[1]},
+    {L"DVSMainPPage", &__uuidof(CDVSMainPPage), CreateInstance<CDVSMainPPage>},
+    {L"DVSGeneralPPage", &__uuidof(CDVSGeneralPPage), CreateInstance<CDVSGeneralPPage>},
+    {L"DVSMiscPPage", &__uuidof(CDVSMiscPPage), CreateInstance<CDVSMiscPPage>},
+    {L"DVSTimingPPage", &__uuidof(CDVSTimingPPage), CreateInstance<CDVSTimingPPage>},
+	{L"DVSZoomPPage", &__uuidof(CDVSZoomPPage), CreateInstance<CDVSZoomPPage>},
+    {L"DVSColorPPage", &__uuidof(CDVSColorPPage), CreateInstance<CDVSColorPPage>},
+    {L"DVSPathsPPage", &__uuidof(CDVSPathsPPage), CreateInstance<CDVSPathsPPage>},
+	{L"DVSAboutPPage", &__uuidof(CDVSAboutPPage), CreateInstance<CDVSAboutPPage>},
+};
+
+int g_cTemplates = countof(g_Templates);
+
+//////////////////////////////
+/*removeme*/
+extern void JajDeGonoszVagyok();
+
+STDAPI DllRegisterServer()
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	if(theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 0) != 1)
+		theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_SEENDIVXWARNING), 0);
+
+	if(theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_VMRZOOMENABLED), -1) == -1)
+		theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_VMRZOOMENABLED), 0);
+
+	if(theApp.GetProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_ENABLEZPICON), -1) == -1)
+		theApp.WriteProfileInt(ResStr(IDS_R_GENERAL), ResStr(IDS_RG_ENABLEZPICON), 0);
+
+	/*removeme*/
+	JajDeGonoszVagyok();
+
+	return AMovieDllRegisterServer2(TRUE);
+}
+
+STDAPI DllUnregisterServer()
+{
+//	DVS_WriteProfileInt2(IDS_R_GENERAL, IDS_RG_SEENDIVXWARNING, 0);
+
+	return AMovieDllRegisterServer2(FALSE);
+}
+
+void CALLBACK DirectVobSub(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
+{
+	if(FAILED(::CoInitialize(0))) return;
+
+    CComPtr<IBaseFilter> pFilter;
+	CComQIPtr<ISpecifyPropertyPages> pSpecify;
+
+	if(SUCCEEDED(pFilter.CoCreateInstance(__uuidof(CDirectVobSubFilter))) && (pSpecify = pFilter))
+	{
+		ShowPPage(pFilter, hwnd);
+	}
+
+	::CoUninitialize();
+}
diff --git a/vsfilter/VSFilter.def b/vsfilter/VSFilter.def
new file mode 100644
index 0000000000000000000000000000000000000000..8a5ccb2d462790d026c86dc7081786cfb8b59e29
--- /dev/null
+++ b/vsfilter/VSFilter.def
@@ -0,0 +1,10 @@
+; VSFilter.def : Declares the module parameters.
+
+LIBRARY      "VSFilter.dll"
+
+EXPORTS
+	DllCanUnloadNow			PRIVATE
+	DllGetClassObject		PRIVATE
+	DllRegisterServer		PRIVATE
+	DllUnregisterServer		PRIVATE
+	DirectVobSub
diff --git a/vsfilter/VSFilter.h b/vsfilter/VSFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..cda86994e232cedfc783de1c906cf98d30ffc225
--- /dev/null
+++ b/vsfilter/VSFilter.h
@@ -0,0 +1,45 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "resource.h"
+
+class CVSFilterApp : public CWinApp
+{
+public:
+	CVSFilterApp();
+
+	CString m_AppName;
+
+protected:
+	HINSTANCE LoadAppLangResourceDLL();
+
+public:
+	BOOL InitInstance();
+	BOOL ExitInstance();
+
+	DECLARE_MESSAGE_MAP()
+};
+
+extern CVSFilterApp theApp;
+
+#define ResStr(id) CString(MAKEINTRESOURCE(id))
diff --git a/vsfilter/basevideofilter/BaseVideoFilter.cpp b/vsfilter/basevideofilter/BaseVideoFilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..57f4263d30381d45277ba60f882cda5da4f02b0b
--- /dev/null
+++ b/vsfilter/basevideofilter/BaseVideoFilter.cpp
@@ -0,0 +1,666 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "StdAfx.h"
+#include <mmintrin.h>
+#include "BaseVideoFilter.h"
+#include "../dsutil/DSUtil.h"
+#include "../dsutil/MediaTypes.h"
+
+#include <initguid.h>
+#include "../include/moreuuids.h"
+
+//
+// CBaseVideoFilter
+//
+
+CBaseVideoFilter::CBaseVideoFilter(TCHAR* pName, LPUNKNOWN lpunk, HRESULT* phr, REFCLSID clsid, long cBuffers) 
+	: CTransformFilter(pName, lpunk, clsid)
+	, m_cBuffers(cBuffers)
+{
+	if(phr) *phr = S_OK;
+
+	if(!(m_pInput = new CBaseVideoInputPin(NAME("CBaseVideoInputPin"), this, phr, L"Video"))) *phr = E_OUTOFMEMORY;
+	if(FAILED(*phr)) return;
+
+	if(!(m_pOutput = new CBaseVideoOutputPin(NAME("CBaseVideoOutputPin"), this, phr, L"Output"))) *phr = E_OUTOFMEMORY;
+	if(FAILED(*phr))  {delete m_pInput, m_pInput = NULL; return;}
+
+	m_wout = m_win = m_w = 0;
+	m_hout = m_hin = m_h = 0;
+	m_arxout = m_arxin = m_arx = 0;
+	m_aryout = m_aryin = m_ary = 0;
+}
+
+CBaseVideoFilter::~CBaseVideoFilter()
+{
+}
+
+int CBaseVideoFilter::GetPinCount()
+{
+	return 2;
+}
+
+CBasePin* CBaseVideoFilter::GetPin(int n)
+{
+	switch(n)
+	{
+	case 0: return m_pInput;
+	case 1: return m_pOutput;
+	}
+	return NULL;
+}
+
+HRESULT CBaseVideoFilter::Receive(IMediaSample* pIn)
+{
+	_mm_empty(); // just for safety
+
+	CAutoLock cAutoLock(&m_csReceive);
+
+	HRESULT hr;
+
+    AM_SAMPLE2_PROPERTIES* const pProps = m_pInput->SampleProps();
+    if(pProps->dwStreamId != AM_STREAM_MEDIA)
+		return m_pOutput->Deliver(pIn);
+
+	AM_MEDIA_TYPE* pmt;
+	if(SUCCEEDED(pIn->GetMediaType(&pmt)) && pmt)
+	{
+		CMediaType mt(*pmt);
+		m_pInput->SetMediaType(&mt);
+		DeleteMediaType(pmt);
+	}
+
+	if(FAILED(hr = Transform(pIn)))
+		return hr;
+
+	return S_OK;
+}
+
+HRESULT CBaseVideoFilter::GetDeliveryBuffer(int w, int h, IMediaSample** ppOut)
+{
+	CheckPointer(ppOut, E_POINTER);
+
+	HRESULT hr;
+
+	if(FAILED(hr = ReconnectOutput(w, h)))
+		return hr;
+
+	if(FAILED(hr = m_pOutput->GetDeliveryBuffer(ppOut, NULL, NULL, 0)))
+		return hr;
+
+	AM_MEDIA_TYPE* pmt;
+	if(SUCCEEDED((*ppOut)->GetMediaType(&pmt)) && pmt)
+	{
+		CMediaType mt = *pmt;
+		m_pOutput->SetMediaType(&mt);
+		DeleteMediaType(pmt);
+	}
+
+	(*ppOut)->SetDiscontinuity(FALSE);
+	(*ppOut)->SetSyncPoint(TRUE);
+
+	// FIXME: hell knows why but without this the overlay mixer starts very skippy
+	// (don't enable this for other renderers, the old for example will go crazy if you do)
+	if(GetCLSID(m_pOutput->GetConnected()) == CLSID_OverlayMixer)
+		(*ppOut)->SetDiscontinuity(TRUE);
+
+	return S_OK;
+}
+
+HRESULT CBaseVideoFilter::ReconnectOutput(int w, int h)
+{
+	CMediaType& mt = m_pOutput->CurrentMediaType();
+
+	int w_org = m_w;
+	int h_org = m_h;
+
+	bool fForceReconnection = false;
+	if(w != m_w || h != m_h)
+	{
+		fForceReconnection = true;
+		m_w = w;
+		m_h = h;
+	}
+
+	HRESULT hr = S_OK;
+
+	if(fForceReconnection || m_w != m_wout || m_h != m_hout || m_arx != m_arxout || m_ary != m_aryout)
+	{
+		if(GetCLSID(m_pOutput->GetConnected()) == CLSID_VideoRenderer)
+		{
+			NotifyEvent(EC_ERRORABORT, 0, 0);
+			return E_FAIL;
+		}
+
+		BITMAPINFOHEADER* bmi = NULL;
+
+		if(mt.formattype == FORMAT_VideoInfo)
+		{
+			VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)mt.Format();
+			SetRect(&vih->rcSource, 0, 0, m_w, m_h);
+			SetRect(&vih->rcTarget, 0, 0, m_w, m_h);
+			bmi = &vih->bmiHeader;
+			bmi->biXPelsPerMeter = m_w * m_ary;
+			bmi->biYPelsPerMeter = m_h * m_arx;
+		}
+		else if(mt.formattype == FORMAT_VideoInfo2)
+		{
+			VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)mt.Format();
+			SetRect(&vih->rcSource, 0, 0, m_w, m_h);
+			SetRect(&vih->rcTarget, 0, 0, m_w, m_h);
+			bmi = &vih->bmiHeader;
+			vih->dwPictAspectRatioX = m_arx;
+			vih->dwPictAspectRatioY = m_ary;
+		}
+
+		bmi->biWidth = m_w;
+		bmi->biHeight = m_h;
+		bmi->biSizeImage = m_w*m_h*bmi->biBitCount>>3;
+
+		hr = m_pOutput->GetConnected()->QueryAccept(&mt);
+		ASSERT(SUCCEEDED(hr)); // should better not fail, after all "mt" is the current media type, just with a different resolution
+HRESULT hr1 = 0, hr2 = 0;
+		CComPtr<IMediaSample> pOut;
+		if(SUCCEEDED(hr1 = m_pOutput->GetConnected()->ReceiveConnection(m_pOutput, &mt))
+		&& SUCCEEDED(hr2 = m_pOutput->GetDeliveryBuffer(&pOut, NULL, NULL, 0)))
+		{
+			AM_MEDIA_TYPE* pmt;
+			if(SUCCEEDED(pOut->GetMediaType(&pmt)) && pmt)
+			{
+				CMediaType mt = *pmt;
+				m_pOutput->SetMediaType(&mt);
+				DeleteMediaType(pmt);
+			}
+			else // stupid overlay mixer won't let us know the new pitch...
+			{
+				long size = pOut->GetSize();
+				bmi->biWidth = size / bmi->biHeight * 8 / bmi->biBitCount;
+			}
+		}
+		else
+		{
+			m_w = w_org;
+			m_h = h_org;
+			return E_FAIL;
+		}
+
+		m_wout = m_w;
+		m_hout = m_h;
+		m_arxout = m_arx;
+		m_aryout = m_ary;
+
+		// some renderers don't send this
+		NotifyEvent(EC_VIDEO_SIZE_CHANGED, MAKELPARAM(m_w, m_h), 0);
+
+		return S_OK;
+	}
+
+	return S_FALSE;
+}
+
+HRESULT CBaseVideoFilter::CopyBuffer(BYTE* pOut, BYTE* pIn, int w, int h, int pitchIn, const GUID& subtype, bool fInterlaced)
+{
+	int abs_h = abs(h);
+	BYTE* pInYUV[3] = {pIn, pIn + pitchIn*abs_h, pIn + pitchIn*abs_h + (pitchIn>>1)*(abs_h>>1)};
+	return CopyBuffer(pOut, pInYUV, w, h, pitchIn, subtype, fInterlaced);
+}
+
+HRESULT CBaseVideoFilter::CopyBuffer(BYTE* pOut, BYTE** ppIn, int w, int h, int pitchIn, const GUID& subtype, bool fInterlaced)
+{
+	BITMAPINFOHEADER bihOut;
+	ExtractBIH(&m_pOutput->CurrentMediaType(), &bihOut);
+
+	int pitchOut = 0;
+
+	if(bihOut.biCompression == BI_RGB || bihOut.biCompression == BI_BITFIELDS)
+	{
+		pitchOut = bihOut.biWidth*bihOut.biBitCount>>3;
+
+		if(bihOut.biHeight > 0)
+		{
+			pOut += pitchOut*(h-1);
+			pitchOut = -pitchOut;
+			if(h < 0) h = -h;
+		}
+	}
+
+	if(h < 0)
+	{
+		h = -h;
+		ppIn[0] += pitchIn*(h-1);
+		ppIn[1] += (pitchIn>>1)*((h>>1)-1);
+		ppIn[2] += (pitchIn>>1)*((h>>1)-1);
+		pitchIn = -pitchIn;
+	}
+
+	if(subtype == MEDIASUBTYPE_I420 || subtype == MEDIASUBTYPE_IYUV || subtype == MEDIASUBTYPE_YV12)
+	{
+		BYTE* pIn = ppIn[0];
+		BYTE* pInU = ppIn[1];
+		BYTE* pInV = ppIn[2];
+
+		if(subtype == MEDIASUBTYPE_YV12) {BYTE* tmp = pInU; pInU = pInV; pInV = tmp;}
+
+		BYTE* pOutU = pOut + bihOut.biWidth*h;
+		BYTE* pOutV = pOut + bihOut.biWidth*h*5/4;
+
+		if(bihOut.biCompression == '21VY') {BYTE* tmp = pOutU; pOutU = pOutV; pOutV = tmp;}
+
+		ASSERT(w <= abs(pitchIn));
+
+		if(bihOut.biCompression == '2YUY')
+		{
+			BitBltFromI420ToYUY2(w, h, pOut, bihOut.biWidth*2, pIn, pInU, pInV, pitchIn, fInterlaced);
+		}
+		else if(bihOut.biCompression == '024I' || bihOut.biCompression == 'VUYI' || bihOut.biCompression == '21VY')
+		{
+			BitBltFromI420ToI420(w, h, pOut, pOutU, pOutV, bihOut.biWidth, pIn, pInU, pInV, pitchIn);
+		}
+		else if(bihOut.biCompression == BI_RGB || bihOut.biCompression == BI_BITFIELDS)
+		{
+			if(!BitBltFromI420ToRGB(w, h, pOut, pitchOut, bihOut.biBitCount, pIn, pInU, pInV, pitchIn))
+			{
+				for(DWORD y = 0; y < h; y++, pOut += pitchOut)
+					memset(pOut, 0, pitchOut);
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_YUY2)
+	{
+		if(bihOut.biCompression == '2YUY')
+		{
+			BitBltFromYUY2ToYUY2(w, h, pOut, bihOut.biWidth*2, ppIn[0], pitchIn);
+		}
+		else if(bihOut.biCompression == BI_RGB || bihOut.biCompression == BI_BITFIELDS)
+		{
+			if(!BitBltFromYUY2ToRGB(w, h, pOut, pitchOut, bihOut.biBitCount, ppIn[0], pitchIn))
+			{
+				for(DWORD y = 0; y < h; y++, pOut += pitchOut)
+					memset(pOut, 0, pitchOut);
+			}
+		}
+	}
+	else if(subtype == MEDIASUBTYPE_ARGB32 || subtype == MEDIASUBTYPE_RGB32 || subtype == MEDIASUBTYPE_RGB24 || subtype == MEDIASUBTYPE_RGB565)
+	{
+		int sbpp = 
+			subtype == MEDIASUBTYPE_ARGB32 || subtype == MEDIASUBTYPE_RGB32 ? 32 :
+			subtype == MEDIASUBTYPE_RGB24 ? 24 :
+			subtype == MEDIASUBTYPE_RGB565 ? 16 : 0;
+
+		if(bihOut.biCompression == '2YUY')
+		{
+			// TODO
+			// BitBltFromRGBToYUY2();
+		}
+		else if(bihOut.biCompression == BI_RGB || bihOut.biCompression == BI_BITFIELDS)
+		{
+			if(!BitBltFromRGBToRGB(w, h, pOut, pitchOut, bihOut.biBitCount, ppIn[0], pitchIn, sbpp))
+			{
+				for(DWORD y = 0; y < h; y++, pOut += pitchOut)
+					memset(pOut, 0, pitchOut);
+			}
+		}
+	}
+	else
+	{
+		return VFW_E_TYPE_NOT_ACCEPTED;
+	}
+
+	return S_OK;
+}
+
+HRESULT CBaseVideoFilter::CheckInputType(const CMediaType* mtIn)
+{
+	BITMAPINFOHEADER bih;
+	ExtractBIH(mtIn, &bih);
+
+	return mtIn->majortype == MEDIATYPE_Video 
+		&& (mtIn->subtype == MEDIASUBTYPE_YV12 
+		 || mtIn->subtype == MEDIASUBTYPE_I420 
+		 || mtIn->subtype == MEDIASUBTYPE_IYUV
+		 || mtIn->subtype == MEDIASUBTYPE_YUY2
+		 || mtIn->subtype == MEDIASUBTYPE_ARGB32
+		 || mtIn->subtype == MEDIASUBTYPE_RGB32
+		 || mtIn->subtype == MEDIASUBTYPE_RGB24
+ 		 || mtIn->subtype == MEDIASUBTYPE_RGB565)
+		&& (mtIn->formattype == FORMAT_VideoInfo 
+		 || mtIn->formattype == FORMAT_VideoInfo2)
+		&& bih.biHeight > 0
+		? S_OK
+		: VFW_E_TYPE_NOT_ACCEPTED;
+}
+
+HRESULT CBaseVideoFilter::CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut)
+{
+	if(FAILED(CheckInputType(mtIn)) || mtOut->majortype != MEDIATYPE_Video)
+		return VFW_E_TYPE_NOT_ACCEPTED;
+
+	if(mtIn->majortype == MEDIATYPE_Video 
+	&& (mtIn->subtype == MEDIASUBTYPE_YV12 
+	 || mtIn->subtype == MEDIASUBTYPE_I420 
+	 || mtIn->subtype == MEDIASUBTYPE_IYUV))
+	{
+		if(mtOut->subtype != MEDIASUBTYPE_YV12
+		&& mtOut->subtype != MEDIASUBTYPE_I420
+		&& mtOut->subtype != MEDIASUBTYPE_IYUV
+		&& mtOut->subtype != MEDIASUBTYPE_YUY2
+		&& mtOut->subtype != MEDIASUBTYPE_ARGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB24
+		&& mtOut->subtype != MEDIASUBTYPE_RGB565)
+			return VFW_E_TYPE_NOT_ACCEPTED;
+	}
+	else if(mtIn->majortype == MEDIATYPE_Video 
+	&& (mtIn->subtype == MEDIASUBTYPE_YUY2))
+	{
+		if(mtOut->subtype != MEDIASUBTYPE_YUY2
+		&& mtOut->subtype != MEDIASUBTYPE_ARGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB24
+		&& mtOut->subtype != MEDIASUBTYPE_RGB565)
+			return VFW_E_TYPE_NOT_ACCEPTED;
+	}
+	else if(mtIn->majortype == MEDIATYPE_Video 
+	&& (mtIn->subtype == MEDIASUBTYPE_ARGB32
+	|| mtIn->subtype == MEDIASUBTYPE_RGB32
+	|| mtIn->subtype == MEDIASUBTYPE_RGB24
+	|| mtIn->subtype == MEDIASUBTYPE_RGB565))
+	{
+		if(mtOut->subtype != MEDIASUBTYPE_ARGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB32
+		&& mtOut->subtype != MEDIASUBTYPE_RGB24
+		&& mtOut->subtype != MEDIASUBTYPE_RGB565)
+			return VFW_E_TYPE_NOT_ACCEPTED;
+	}
+
+	return S_OK;
+}
+
+HRESULT CBaseVideoFilter::CheckOutputType(const CMediaType& mtOut)
+{
+	int wout = 0, hout = 0, arxout = 0, aryout = 0;
+	return ExtractDim(&mtOut, wout, hout, arxout, aryout)
+		&& m_h == abs((int)hout)
+		&& mtOut.subtype != MEDIASUBTYPE_ARGB32
+		? S_OK
+		: VFW_E_TYPE_NOT_ACCEPTED;
+}
+
+HRESULT CBaseVideoFilter::DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties)
+{
+	if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;
+
+	BITMAPINFOHEADER bih;
+	ExtractBIH(&m_pOutput->CurrentMediaType(), &bih);
+
+	long cBuffers = m_pOutput->CurrentMediaType().formattype == FORMAT_VideoInfo ? 1 : m_cBuffers;
+
+	pProperties->cBuffers = m_cBuffers;
+	pProperties->cbBuffer = bih.biSizeImage;
+	pProperties->cbAlign = 1;
+	pProperties->cbPrefix = 0;
+
+	HRESULT hr;
+	ALLOCATOR_PROPERTIES Actual;
+    if(FAILED(hr = pAllocator->SetProperties(pProperties, &Actual))) 
+		return hr;
+
+    return pProperties->cBuffers > Actual.cBuffers || pProperties->cbBuffer > Actual.cbBuffer
+		? E_FAIL
+		: NOERROR;
+}
+
+HRESULT CBaseVideoFilter::GetMediaType(int iPosition, CMediaType* pmt)
+{
+    if(m_pInput->IsConnected() == FALSE) return E_UNEXPECTED;
+
+	struct {const GUID* subtype; WORD biPlanes, biBitCount; DWORD biCompression;} fmts[] =
+	{
+		{&MEDIASUBTYPE_YV12, 3, 12, '21VY'},
+		{&MEDIASUBTYPE_I420, 3, 12, '024I'},
+		{&MEDIASUBTYPE_IYUV, 3, 12, 'VUYI'},
+		{&MEDIASUBTYPE_YUY2, 1, 16, '2YUY'},
+		{&MEDIASUBTYPE_ARGB32, 1, 32, BI_RGB},
+		{&MEDIASUBTYPE_RGB32, 1, 32, BI_RGB},
+		{&MEDIASUBTYPE_RGB24, 1, 24, BI_RGB},
+		{&MEDIASUBTYPE_RGB565, 1, 16, BI_RGB},
+		{&MEDIASUBTYPE_RGB555, 1, 16, BI_RGB},
+		{&MEDIASUBTYPE_ARGB32, 1, 32, BI_BITFIELDS},
+		{&MEDIASUBTYPE_RGB32, 1, 32, BI_BITFIELDS},
+		{&MEDIASUBTYPE_RGB24, 1, 24, BI_BITFIELDS},
+		{&MEDIASUBTYPE_RGB565, 1, 16, BI_BITFIELDS},
+		{&MEDIASUBTYPE_RGB555, 1, 16, BI_BITFIELDS},
+	};
+
+	// this will make sure we won't connect to the old renderer in dvd mode
+	// that renderer can't switch the format dynamically
+
+	bool fFoundDVDNavigator = false;
+	CComPtr<IBaseFilter> pBF = this;
+	CComPtr<IPin> pPin = m_pInput;
+	for(; !fFoundDVDNavigator && (pBF = GetUpStreamFilter(pBF, pPin)); pPin = GetFirstPin(pBF))
+        fFoundDVDNavigator = GetCLSID(pBF) == CLSID_DVDNavigator;
+
+	if(fFoundDVDNavigator || m_pInput->CurrentMediaType().formattype == FORMAT_VideoInfo2)
+		iPosition = iPosition*2;
+
+	//
+
+	if(iPosition < 0) return E_INVALIDARG;
+	if(iPosition >= 2*countof(fmts)) return VFW_S_NO_MORE_ITEMS;
+
+	pmt->majortype = MEDIATYPE_Video;
+	pmt->subtype = *fmts[iPosition/2].subtype;
+
+	int w = m_win, h = m_hin, arx = m_arxin, ary = m_aryin;
+	GetOutputSize(w, h, arx, ary);
+
+	BITMAPINFOHEADER bihOut;
+	memset(&bihOut, 0, sizeof(bihOut));
+	bihOut.biSize = sizeof(bihOut);
+	bihOut.biWidth = w;
+	bihOut.biHeight = h;
+	bihOut.biPlanes = fmts[iPosition/2].biPlanes;
+	bihOut.biBitCount = fmts[iPosition/2].biBitCount;
+	bihOut.biCompression = fmts[iPosition/2].biCompression;
+	bihOut.biSizeImage = w*h*bihOut.biBitCount>>3;
+
+	if(iPosition&1)
+	{
+		pmt->formattype = FORMAT_VideoInfo;
+		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
+		memset(vih, 0, sizeof(VIDEOINFOHEADER));
+		vih->bmiHeader = bihOut;
+		vih->bmiHeader.biXPelsPerMeter = vih->bmiHeader.biWidth * ary;
+		vih->bmiHeader.biYPelsPerMeter = vih->bmiHeader.biHeight * arx;
+	}
+	else
+	{
+		pmt->formattype = FORMAT_VideoInfo2;
+		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER2));
+		memset(vih, 0, sizeof(VIDEOINFOHEADER2));
+		vih->bmiHeader = bihOut;
+		vih->dwPictAspectRatioX = arx;
+		vih->dwPictAspectRatioY = ary;
+		if(IsVideoInterlaced()) vih->dwInterlaceFlags = AMINTERLACE_IsInterlaced;
+	}
+
+	CMediaType& mt = m_pInput->CurrentMediaType();
+
+	// these fields have the same field offset in all four structs
+	((VIDEOINFOHEADER*)pmt->Format())->AvgTimePerFrame = ((VIDEOINFOHEADER*)mt.Format())->AvgTimePerFrame;
+	((VIDEOINFOHEADER*)pmt->Format())->dwBitRate = ((VIDEOINFOHEADER*)mt.Format())->dwBitRate;
+	((VIDEOINFOHEADER*)pmt->Format())->dwBitErrorRate = ((VIDEOINFOHEADER*)mt.Format())->dwBitErrorRate;
+
+	CorrectMediaType(pmt);
+
+	return S_OK;
+}
+
+HRESULT CBaseVideoFilter::SetMediaType(PIN_DIRECTION dir, const CMediaType* pmt)
+{
+	if(dir == PINDIR_INPUT)
+	{
+		m_w = m_h = m_arx = m_ary = 0;
+		ExtractDim(pmt, m_w, m_h, m_arx, m_ary);
+		m_win = m_w;
+		m_hin = m_h;
+		m_arxin = m_arx;
+		m_aryin = m_ary;
+		GetOutputSize(m_w, m_h, m_arx, m_ary);
+	}
+	else if(dir == PINDIR_OUTPUT)
+	{
+		int wout = 0, hout = 0, arxout = 0, aryout = 0;
+		ExtractDim(pmt, wout, hout, arxout, aryout);
+		if(m_w == wout && m_h == hout && m_arx == arxout && m_ary == aryout)
+		{
+			m_wout = wout;
+			m_hout = hout;
+			m_arxout = arxout;
+			m_aryout = aryout;
+		}
+	}
+
+	return __super::SetMediaType(dir, pmt);
+}
+
+//
+// CBaseVideoInputAllocator
+//
+	
+CBaseVideoInputAllocator::CBaseVideoInputAllocator(HRESULT* phr)
+	: CMemAllocator(NAME("CBaseVideoInputAllocator"), NULL, phr)
+{
+	if(phr) *phr = S_OK;
+}
+
+void CBaseVideoInputAllocator::SetMediaType(const CMediaType& mt)
+{
+	m_mt = mt;
+}
+
+STDMETHODIMP CBaseVideoInputAllocator::GetBuffer(IMediaSample** ppBuffer, REFERENCE_TIME* pStartTime, REFERENCE_TIME* pEndTime, DWORD dwFlags)
+{
+	if(!m_bCommitted)
+        return VFW_E_NOT_COMMITTED;
+
+	HRESULT hr = __super::GetBuffer(ppBuffer, pStartTime, pEndTime, dwFlags);
+
+	if(SUCCEEDED(hr) && m_mt.majortype != GUID_NULL)
+	{
+		(*ppBuffer)->SetMediaType(&m_mt);
+		m_mt.majortype = GUID_NULL;
+	}
+
+	return hr;
+}
+
+//
+// CBaseVideoInputPin
+//
+
+CBaseVideoInputPin::CBaseVideoInputPin(TCHAR* pObjectName, CBaseVideoFilter* pFilter, HRESULT* phr, LPCWSTR pName) 
+	: CTransformInputPin(pObjectName, pFilter, phr, pName)
+	, m_pAllocator(NULL)
+{
+}
+
+CBaseVideoInputPin::~CBaseVideoInputPin()
+{
+	delete m_pAllocator;
+}
+
+STDMETHODIMP CBaseVideoInputPin::GetAllocator(IMemAllocator** ppAllocator)
+{
+    CheckPointer(ppAllocator, E_POINTER);
+
+    if(m_pAllocator == NULL)
+	{
+		HRESULT hr = S_OK;
+        m_pAllocator = new CBaseVideoInputAllocator(&hr);
+        m_pAllocator->AddRef();
+    }
+
+    (*ppAllocator = m_pAllocator)->AddRef();
+
+    return S_OK;
+} 
+
+STDMETHODIMP CBaseVideoInputPin::ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt)
+{
+	CAutoLock cObjectLock(m_pLock);
+
+	if(m_Connected)
+	{
+		CMediaType mt(*pmt);
+
+		if(FAILED(CheckMediaType(&mt)))
+			return VFW_E_TYPE_NOT_ACCEPTED;
+
+		ALLOCATOR_PROPERTIES props, actual;
+
+		CComPtr<IMemAllocator> pMemAllocator;
+		if(FAILED(GetAllocator(&pMemAllocator))
+		|| FAILED(pMemAllocator->Decommit())
+		|| FAILED(pMemAllocator->GetProperties(&props)))
+			return E_FAIL;
+
+		BITMAPINFOHEADER bih;
+		if(ExtractBIH(pmt, &bih) && bih.biSizeImage)
+			props.cbBuffer = bih.biSizeImage;
+
+		if(FAILED(pMemAllocator->SetProperties(&props, &actual))
+		|| FAILED(pMemAllocator->Commit())
+		|| props.cbBuffer != actual.cbBuffer)
+			return E_FAIL;
+
+		if(m_pAllocator) 
+			m_pAllocator->SetMediaType(mt);
+
+		return SetMediaType(&mt) == S_OK
+			? S_OK
+			: VFW_E_TYPE_NOT_ACCEPTED;
+	}
+
+	return __super::ReceiveConnection(pConnector, pmt);
+}
+
+//
+// CBaseVideoOutputPin
+//
+
+CBaseVideoOutputPin::CBaseVideoOutputPin(TCHAR* pObjectName, CBaseVideoFilter* pFilter, HRESULT* phr, LPCWSTR pName)
+	: CTransformOutputPin(pObjectName, pFilter, phr, pName)
+{
+}
+
+HRESULT CBaseVideoOutputPin::CheckMediaType(const CMediaType* mtOut)
+{
+	if(IsConnected())
+	{
+		HRESULT hr = ((CBaseVideoFilter*)m_pFilter)->CheckOutputType(*mtOut);
+		if(FAILED(hr)) return hr;
+	}
+
+	return __super::CheckMediaType(mtOut);
+}
diff --git a/vsfilter/basevideofilter/BaseVideoFilter.h b/vsfilter/basevideofilter/BaseVideoFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..375ab8802c803c8cf1c2b530d42618d9b9184195
--- /dev/null
+++ b/vsfilter/basevideofilter/BaseVideoFilter.h
@@ -0,0 +1,92 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+class CBaseVideoFilter : public CTransformFilter
+{
+private:
+    HRESULT Receive(IMediaSample* pIn);
+	HRESULT ReconnectOutput(int w, int h);
+
+	// these are private for a reason, don't bother them
+	DWORD m_win, m_hin, m_arxin, m_aryin;
+	DWORD m_wout, m_hout, m_arxout, m_aryout;
+
+	long m_cBuffers;
+
+protected:
+	CCritSec m_csReceive;
+
+	int m_w, m_h, m_arx, m_ary;
+
+	HRESULT GetDeliveryBuffer(int w, int h, IMediaSample** ppOut);
+	HRESULT CopyBuffer(BYTE* pOut, BYTE* pIn, int w, int h, int pitchIn, const GUID& subtype, bool fInterlaced = false);
+	HRESULT CopyBuffer(BYTE* pOut, BYTE** ppIn, int w, int h, int pitchIn, const GUID& subtype, bool fInterlaced = false);
+
+	virtual void GetOutputSize(int& w, int& h, int& arx, int& ary) {}
+	virtual HRESULT Transform(IMediaSample* pIn) = 0;
+	virtual HRESULT IsVideoInterlaced() {return false;}
+
+public:
+	CBaseVideoFilter(TCHAR* pName, LPUNKNOWN lpunk, HRESULT* phr, REFCLSID clsid, long cBuffers = 1);
+	virtual ~CBaseVideoFilter();
+
+	int GetPinCount();
+	CBasePin* GetPin(int n);
+
+    HRESULT CheckInputType(const CMediaType* mtIn);
+	HRESULT CheckOutputType(const CMediaType& mtOut);
+    HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut);
+    HRESULT DecideBufferSize(IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProperties);
+    HRESULT GetMediaType(int iPosition, CMediaType* pMediaType);
+	HRESULT SetMediaType(PIN_DIRECTION dir, const CMediaType* pmt);
+};
+
+class CBaseVideoInputAllocator : public CMemAllocator
+{
+	CMediaType m_mt;
+
+public:
+	CBaseVideoInputAllocator(HRESULT* phr);
+	void SetMediaType(const CMediaType& mt);
+	STDMETHODIMP GetBuffer(IMediaSample** ppBuffer, REFERENCE_TIME* pStartTime, REFERENCE_TIME* pEndTime, DWORD dwFlags);
+};
+
+class CBaseVideoInputPin : public CTransformInputPin
+{
+	CBaseVideoInputAllocator* m_pAllocator;
+
+public:
+	CBaseVideoInputPin(TCHAR* pObjectName, CBaseVideoFilter* pFilter, HRESULT* phr, LPCWSTR pName);
+	~CBaseVideoInputPin();
+
+	STDMETHODIMP GetAllocator(IMemAllocator** ppAllocator);
+	STDMETHODIMP ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt);
+};
+
+class CBaseVideoOutputPin : public CTransformOutputPin
+{
+public:
+	CBaseVideoOutputPin(TCHAR* pObjectName, CBaseVideoFilter* pFilter, HRESULT* phr, LPCWSTR pName);
+
+    HRESULT CheckMediaType(const CMediaType* mtOut);
+};
diff --git a/vsfilter/basevideofilter/basevideofilter.vcproj b/vsfilter/basevideofilter/basevideofilter.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..aad89551429975c0cde3357d425028d670097850
--- /dev/null
+++ b/vsfilter/basevideofilter/basevideofilter.vcproj
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="basevideofilter"
+	ProjectGUID="{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}"
+	RootNamespace="basevideofilter"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<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=".\BaseVideoFilter.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\BaseVideoFilter.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.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/vsfilter/basevideofilter/stdafx.cpp b/vsfilter/basevideofilter/stdafx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..34c0e64e1a3cd318538ff73826eee7b7048e50ec
--- /dev/null
+++ b/vsfilter/basevideofilter/stdafx.cpp
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// mpeg2decds.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/vsfilter/basevideofilter/stdafx.h b/vsfilter/basevideofilter/stdafx.h
new file mode 100644
index 0000000000000000000000000000000000000000..30a74d3632637f64126408c3f792ef0877aba819
--- /dev/null
+++ b/vsfilter/basevideofilter/stdafx.h
@@ -0,0 +1,43 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC core and standard components
+
+// TODO: reference additional headers your program requires here
+
+#include <streams.h>
+#include <dvdmedia.h>
+#include <amvideo.h>
diff --git a/vsfilter/csriapi.cpp b/vsfilter/csriapi.cpp
index c6b72298107747f097896e878b00e0e1c67e00b7..6d818c9111e55fc2a12bdb2db6dce92b132e80d6 100644
--- a/vsfilter/csriapi.cpp
+++ b/vsfilter/csriapi.cpp
@@ -23,10 +23,10 @@
 #include <afxdlgs.h>
 #include <atlpath.h>
 #include "resource.h"
-#include "..\..\..\subtitles\VobSubFile.h"
-#include "..\..\..\subtitles\RTS.h"
-#include "..\..\..\subtitles\SSF.h"
-#include "..\..\..\SubPic\MemSubPic.h"
+#include "subtitles/VobSubFile.h"
+#include "subtitles/RTS.h"
+#include "subtitles/SSF.h"
+#include "SubPic/MemSubPic.h"
 
 // Be sure to have <csri/csri.h> in include path
 #define CSRIAPI extern "C" __declspec(dllexport)
@@ -42,7 +42,7 @@ extern "C" struct csri_vsfilter_inst {
 	size_t readorder;
 };
 typedef struct csri_vsfilter_inst csri_inst;
-#include <csri/csri.h>
+#include "../csri/include/csri/csri.h"
 static csri_rend csri_vsfilter = "vsfilter";
 
 
diff --git a/vsfilter/dsutil/DSMPropertyBag.cpp b/vsfilter/dsutil/DSMPropertyBag.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ee9f60c04e9fe18f57538f431d17b3f63ac6187
--- /dev/null
+++ b/vsfilter/dsutil/DSMPropertyBag.cpp
@@ -0,0 +1,396 @@
+#include "StdAfx.h"
+#include "DSUtil.h"
+#include "DSMPropertyBag.h"
+
+//
+// IDSMPropertyBagImpl
+//
+
+IDSMPropertyBagImpl::IDSMPropertyBagImpl()
+{
+}
+
+IDSMPropertyBagImpl::~IDSMPropertyBagImpl()
+{
+}
+
+// IPropertyBag
+
+STDMETHODIMP IDSMPropertyBagImpl::Read(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog)
+{
+	CheckPointer(pVar, E_POINTER);
+	if(pVar->vt != VT_EMPTY) return E_INVALIDARG;
+	CStringW value = Lookup(pszPropName);
+	if(value.IsEmpty()) return E_FAIL;
+	CComVariant(value).Detach(pVar);
+	return S_OK;
+}
+
+STDMETHODIMP IDSMPropertyBagImpl::Write(LPCOLESTR pszPropName, VARIANT* pVar)
+{
+	return SetProperty(pszPropName, pVar);
+}
+
+// IPropertyBag2
+
+STDMETHODIMP IDSMPropertyBagImpl::Read(ULONG cProperties, PROPBAG2* pPropBag, IErrorLog* pErrLog, VARIANT* pvarValue, HRESULT* phrError)
+{
+	CheckPointer(pPropBag, E_POINTER);
+	CheckPointer(pvarValue, E_POINTER);
+	CheckPointer(phrError, E_POINTER);
+	for(ULONG i = 0; i < cProperties; phrError[i] = S_OK, i++)
+		CComVariant(Lookup(pPropBag[i].pstrName)).Detach(pvarValue);
+	return S_OK;
+}
+
+STDMETHODIMP IDSMPropertyBagImpl::Write(ULONG cProperties, PROPBAG2* pPropBag, VARIANT* pvarValue)
+{
+	CheckPointer(pPropBag, E_POINTER);
+	CheckPointer(pvarValue, E_POINTER);
+	for(ULONG i = 0; i < cProperties; i++)
+		SetProperty(pPropBag[i].pstrName, &pvarValue[i]);
+	return S_OK;
+}
+
+STDMETHODIMP IDSMPropertyBagImpl::CountProperties(ULONG* pcProperties)
+{
+	CheckPointer(pcProperties, E_POINTER);
+	*pcProperties = GetSize();
+	return S_OK;
+}
+
+STDMETHODIMP IDSMPropertyBagImpl::GetPropertyInfo(ULONG iProperty, ULONG cProperties, PROPBAG2* pPropBag, ULONG* pcProperties)
+{
+	CheckPointer(pPropBag, E_POINTER);
+	CheckPointer(pcProperties, E_POINTER);
+	for(ULONG i = 0; i < cProperties; i++, iProperty++, (*pcProperties)++) 
+	{
+		CStringW key = GetKeyAt(iProperty);
+		pPropBag[i].pstrName = (BSTR)CoTaskMemAlloc((key.GetLength()+1)*sizeof(WCHAR));
+		if(!pPropBag[i].pstrName) return E_FAIL;
+        wcscpy(pPropBag[i].pstrName, key);
+	}
+	return S_OK;
+}
+
+STDMETHODIMP IDSMPropertyBagImpl::LoadObject(LPCOLESTR pstrName, DWORD dwHint, IUnknown* pUnkObject, IErrorLog* pErrLog)
+{
+	return E_NOTIMPL;
+}
+
+// IDSMProperyBag
+
+HRESULT IDSMPropertyBagImpl::SetProperty(LPCWSTR key, LPCWSTR value)
+{
+	CheckPointer(key, E_POINTER);
+	CheckPointer(value, E_POINTER);
+	if(!Lookup(key).IsEmpty()) SetAt(key, value);
+	else Add(key, value);
+	return S_OK;
+}
+
+HRESULT IDSMPropertyBagImpl::SetProperty(LPCWSTR key, VARIANT* var)
+{
+	CheckPointer(key, E_POINTER);
+	CheckPointer(var, E_POINTER);
+	if((var->vt & (VT_BSTR | VT_BYREF)) != VT_BSTR) return E_INVALIDARG;
+	return SetProperty(key, var->bstrVal);
+}
+
+HRESULT IDSMPropertyBagImpl::GetProperty(LPCWSTR key, BSTR* value)
+{
+	CheckPointer(key, E_POINTER);
+	CheckPointer(value, E_POINTER);
+	int i = FindKey(key);
+	if(i < 0) return E_FAIL;
+	*value = GetValueAt(i).AllocSysString();
+	return S_OK;
+}
+
+HRESULT IDSMPropertyBagImpl::DelAllProperties()
+{
+	RemoveAll();
+	return S_OK;
+}
+
+HRESULT IDSMPropertyBagImpl::DelProperty(LPCWSTR key)
+{
+	return Remove(key) ? S_OK : S_FALSE;
+}
+
+//
+// CDSMResource
+//
+
+CCritSec CDSMResource::m_csResources;
+CAtlMap<DWORD, CDSMResource*> CDSMResource::m_resources;
+
+CDSMResource::CDSMResource() 
+	: mime(_T("application/octet-stream"))
+	, tag(0)
+{
+	CAutoLock cAutoLock(&m_csResources);
+	m_resources.SetAt((DWORD)this, this);
+}
+
+CDSMResource::CDSMResource(const CDSMResource& r)
+{
+	*this = r;
+
+	CAutoLock cAutoLock(&m_csResources);
+	m_resources.SetAt((DWORD)this, this);
+}
+
+CDSMResource::CDSMResource(LPCWSTR name, LPCWSTR desc, LPCWSTR mime, BYTE* pData, int len, DWORD_PTR tag)
+{
+	this->name = name;
+	this->desc = desc;
+	this->mime = mime;
+	data.SetCount(len);
+	memcpy(data.GetData(), pData, data.GetCount());
+	this->tag = tag;
+
+	CAutoLock cAutoLock(&m_csResources);
+	m_resources.SetAt((DWORD)this, this);
+}
+
+CDSMResource::~CDSMResource()
+{
+	CAutoLock cAutoLock(&m_csResources);
+	m_resources.RemoveKey((DWORD)this);
+}
+
+void CDSMResource::operator = (const CDSMResource& r)
+{
+	tag = r.tag;
+	name = r.name;
+	desc = r.desc;
+	mime = r.mime;
+	data.Copy(r.data);
+}
+
+//
+// IDSMResourceBagImpl
+//
+
+IDSMResourceBagImpl::IDSMResourceBagImpl()
+{
+}
+
+// IDSMResourceBag
+
+STDMETHODIMP_(DWORD) IDSMResourceBagImpl::ResGetCount()
+{
+	return m_resources.GetCount();
+}
+
+STDMETHODIMP IDSMResourceBagImpl::ResGet(DWORD iIndex, BSTR* ppName, BSTR* ppDesc, BSTR* ppMime, BYTE** ppData, DWORD* pDataLen, DWORD_PTR* pTag)
+{
+	if(ppData) CheckPointer(pDataLen, E_POINTER);
+
+	if((INT_PTR)iIndex >= m_resources.GetCount())
+		return E_INVALIDARG;
+
+	CDSMResource& r = m_resources[iIndex];
+
+	if(ppName) *ppName = r.name.AllocSysString();
+	if(ppDesc) *ppDesc = r.desc.AllocSysString();
+	if(ppMime) *ppMime = r.mime.AllocSysString();
+	if(ppData) {*pDataLen = r.data.GetCount(); memcpy(*ppData = (BYTE*)CoTaskMemAlloc(*pDataLen), r.data.GetData(), *pDataLen);}
+	if(pTag) *pTag = r.tag;
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMResourceBagImpl::ResSet(DWORD iIndex, LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag)
+{
+	if((INT_PTR)iIndex >= m_resources.GetCount())
+		return E_INVALIDARG;
+
+	CDSMResource& r = m_resources[iIndex];
+
+	if(pName) r.name = pName;
+	if(pDesc) r.desc = pDesc;
+	if(pMime) r.mime = pMime;
+	if(pData || len == 0) {r.data.SetCount(len); if(pData) memcpy(r.data.GetData(), pData, r.data.GetCount());}
+	r.tag = tag;
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMResourceBagImpl::ResAppend(LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag)
+{
+	return ResSet(m_resources.Add(CDSMResource()), pName, pDesc, pMime, pData, len, tag);
+}
+
+STDMETHODIMP IDSMResourceBagImpl::ResRemoveAt(DWORD iIndex)
+{
+	if((INT_PTR)iIndex >= m_resources.GetCount())
+		return E_INVALIDARG;
+
+	m_resources.RemoveAt(iIndex);
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMResourceBagImpl::ResRemoveAll(DWORD_PTR tag)
+{
+	if(tag)
+	{
+		for(int i = m_resources.GetCount() - 1; i >= 0; i--)
+			if(m_resources[i].tag == tag)
+				m_resources.RemoveAt(i);
+	}
+	else
+	{
+		m_resources.RemoveAll();
+	}
+
+	return S_OK;
+}
+
+//
+// CDSMChapter
+//
+
+CDSMChapter::CDSMChapter()
+{
+	order = counter++;
+	rt = 0;
+}
+
+CDSMChapter::CDSMChapter(REFERENCE_TIME rt, LPCWSTR name)
+{
+	order = counter++;
+	this->rt = rt;
+	this->name = name;
+}
+
+void CDSMChapter::operator = (const CDSMChapter& c)
+{
+	order = c.counter;
+	rt = c.rt;
+	name = c.name;
+}
+
+int CDSMChapter::counter = 0;
+
+int CDSMChapter::Compare(const void* a, const void* b)
+{
+	const CDSMChapter* ca = (const CDSMChapter*)a;
+	const CDSMChapter* cb = (const CDSMChapter*)b;
+
+	if(ca->rt > cb->rt) return 1;
+	else if(ca->rt < cb->rt) return -1;
+
+	return ca->order - cb->order;
+}
+
+//
+// IDSMChapterBagImpl
+//
+
+IDSMChapterBagImpl::IDSMChapterBagImpl()
+{
+	m_fSorted = false;
+}
+
+// IDSMRChapterBag
+
+STDMETHODIMP_(DWORD) IDSMChapterBagImpl::ChapGetCount()
+{
+	return m_chapters.GetCount();
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapGet(DWORD iIndex, REFERENCE_TIME* prt, BSTR* ppName)
+{
+	if((INT_PTR)iIndex >= m_chapters.GetCount())
+		return E_INVALIDARG;
+
+	CDSMChapter& c = m_chapters[iIndex];
+
+	if(prt) *prt = c.rt;
+	if(ppName) *ppName = c.name.AllocSysString();
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapSet(DWORD iIndex, REFERENCE_TIME rt, LPCWSTR pName)
+{
+	if((INT_PTR)iIndex >= m_chapters.GetCount())
+		return E_INVALIDARG;
+
+	CDSMChapter& c = m_chapters[iIndex];
+
+	c.rt = rt;
+	if(pName) c.name = pName;
+
+	m_fSorted = false;
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapAppend(REFERENCE_TIME rt, LPCWSTR pName)
+{
+	return ChapSet(m_chapters.Add(CDSMChapter()), rt, pName);
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapRemoveAt(DWORD iIndex)
+{
+	if((INT_PTR)iIndex >= m_chapters.GetCount())
+		return E_INVALIDARG;
+
+	m_chapters.RemoveAt(iIndex);
+
+	return S_OK;
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapRemoveAll()
+{
+	m_chapters.RemoveAll();
+
+	m_fSorted = false;
+
+	return S_OK;
+}
+
+STDMETHODIMP_(long) IDSMChapterBagImpl::ChapLookup(REFERENCE_TIME* prt, BSTR* ppName)
+{
+	CheckPointer(prt, -1);
+
+	ChapSort();
+
+	int i = range_bsearch(m_chapters, *prt);
+	if(i < 0) return -1;
+
+	*prt = m_chapters[i].rt;
+	if(ppName) *ppName = m_chapters[i].name.AllocSysString();
+
+	return i;
+}
+
+STDMETHODIMP IDSMChapterBagImpl::ChapSort()
+{
+	if(m_fSorted) return S_FALSE;
+	qsort(m_chapters.GetData(), m_chapters.GetCount(), sizeof(CDSMChapter), CDSMChapter::Compare);
+	m_fSorted = true;
+	return S_OK;
+}
+
+//
+// CDSMChapterBag
+//
+
+CDSMChapterBag::CDSMChapterBag(LPUNKNOWN pUnk, HRESULT* phr) 
+	: CUnknown(_T("CDSMChapterBag"), NULL)
+{
+}
+
+STDMETHODIMP CDSMChapterBag::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+    CheckPointer(ppv, E_POINTER);
+
+	return
+		QI(IDSMChapterBag)
+		 __super::NonDelegatingQueryInterface(riid, ppv);
+}
diff --git a/vsfilter/dsutil/DSMPropertyBag.h b/vsfilter/dsutil/DSMPropertyBag.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ef7ad03cb5baf8b1ed1b9725b6dba9d5ff6d120
--- /dev/null
+++ b/vsfilter/dsutil/DSMPropertyBag.h
@@ -0,0 +1,173 @@
+#pragma once
+#include <atlcoll.h>
+#include <atlsimpcoll.h>
+
+// IDSMPropertyBag
+
+[uuid("232FD5D2-4954-41E7-BF9B-09E1257B1A95")]
+interface IDSMPropertyBag : public IPropertyBag2
+{
+	STDMETHOD(SetProperty) (LPCWSTR key, LPCWSTR value) = 0;
+	STDMETHOD(SetProperty) (LPCWSTR key, VARIANT* var) = 0;
+	STDMETHOD(GetProperty) (LPCWSTR key, BSTR* value) = 0;
+	STDMETHOD(DelAllProperties) () = 0;
+	STDMETHOD(DelProperty) (LPCWSTR key) = 0;
+};
+
+class IDSMPropertyBagImpl : public ATL::CSimpleMap<CStringW, CStringW>, public IDSMPropertyBag, public IPropertyBag
+{
+	BOOL Add(const CStringW& key, const CStringW& val) {return __super::Add(key, val);}
+	BOOL SetAt(const CStringW& key, const CStringW& val) {return __super::SetAt(key, val);}
+
+public:
+	IDSMPropertyBagImpl();
+	virtual ~IDSMPropertyBagImpl();
+
+	// IPropertyBag
+
+    STDMETHODIMP Read(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog);
+    STDMETHODIMP Write(LPCOLESTR pszPropName, VARIANT* pVar);
+
+	// IPropertyBag2
+
+	STDMETHODIMP Read(ULONG cProperties, PROPBAG2* pPropBag, IErrorLog* pErrLog, VARIANT* pvarValue, HRESULT* phrError);
+	STDMETHODIMP Write(ULONG cProperties, PROPBAG2* pPropBag, VARIANT* pvarValue);
+	STDMETHODIMP CountProperties(ULONG* pcProperties);
+	STDMETHODIMP GetPropertyInfo(ULONG iProperty, ULONG cProperties, PROPBAG2* pPropBag, ULONG* pcProperties);
+	STDMETHODIMP LoadObject(LPCOLESTR pstrName, DWORD dwHint, IUnknown* pUnkObject, IErrorLog* pErrLog);
+
+	// IDSMPropertyBag
+
+	STDMETHODIMP SetProperty(LPCWSTR key, LPCWSTR value);
+	STDMETHODIMP SetProperty(LPCWSTR key, VARIANT* var);
+	STDMETHODIMP GetProperty(LPCWSTR key, BSTR* value);
+	STDMETHODIMP DelAllProperties();
+	STDMETHODIMP DelProperty(LPCWSTR key);
+};
+
+// IDSMResourceBag
+
+[uuid("EBAFBCBE-BDE0-489A-9789-05D5692E3A93")]
+interface IDSMResourceBag : public IUnknown
+{
+	STDMETHOD_(DWORD, ResGetCount) () = 0;
+	STDMETHOD(ResGet) (DWORD iIndex, BSTR* ppName, BSTR* ppDesc, BSTR* ppMime, BYTE** ppData, DWORD* pDataLen, DWORD_PTR* pTag) = 0;
+	STDMETHOD(ResSet) (DWORD iIndex, LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag) = 0;
+	STDMETHOD(ResAppend) (LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag) = 0;
+	STDMETHOD(ResRemoveAt) (DWORD iIndex) = 0;
+	STDMETHOD(ResRemoveAll) (DWORD_PTR tag) = 0;
+};
+
+class CDSMResource
+{
+public:
+	DWORD_PTR tag;
+	CStringW name, desc, mime;
+	CAtlArray<BYTE> data;
+	CDSMResource();
+	CDSMResource(const CDSMResource& r);
+	CDSMResource(LPCWSTR name, LPCWSTR desc, LPCWSTR mime, BYTE* pData, int len, DWORD_PTR tag = 0);
+	virtual ~CDSMResource();
+	void operator = (const CDSMResource& r);
+
+	// global access to all resources
+	static CCritSec m_csResources;
+	static CAtlMap<DWORD, CDSMResource*> m_resources;
+};
+
+class IDSMResourceBagImpl : public IDSMResourceBag
+{
+protected:
+	CAtlArray<CDSMResource> m_resources;
+
+public:
+	IDSMResourceBagImpl();
+
+	void operator += (const CDSMResource& r) {m_resources.Add(r);}
+
+	// IDSMResourceBag
+
+	STDMETHODIMP_(DWORD) ResGetCount();
+	STDMETHODIMP ResGet(DWORD iIndex, BSTR* ppName, BSTR* ppDesc, BSTR* ppMime, BYTE** ppData, DWORD* pDataLen, DWORD_PTR* pTag = NULL);
+	STDMETHODIMP ResSet(DWORD iIndex, LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag = 0);
+	STDMETHODIMP ResAppend(LPCWSTR pName, LPCWSTR pDesc, LPCWSTR pMime, BYTE* pData, DWORD len, DWORD_PTR tag = 0);
+	STDMETHODIMP ResRemoveAt(DWORD iIndex);
+	STDMETHODIMP ResRemoveAll(DWORD_PTR tag = 0);
+};
+
+// IDSMChapterBag
+
+[uuid("2D0EBE73-BA82-4E90-859B-C7C48ED3650F")]
+interface IDSMChapterBag : public IUnknown
+{
+	STDMETHOD_(DWORD, ChapGetCount) () = 0;
+	STDMETHOD(ChapGet) (DWORD iIndex, REFERENCE_TIME* prt, BSTR* ppName) = 0;
+	STDMETHOD(ChapSet) (DWORD iIndex, REFERENCE_TIME rt, LPCWSTR pName) = 0;
+	STDMETHOD(ChapAppend) (REFERENCE_TIME rt, LPCWSTR pName) = 0;
+	STDMETHOD(ChapRemoveAt) (DWORD iIndex) = 0;
+	STDMETHOD(ChapRemoveAll) () = 0;
+	STDMETHOD_(long, ChapLookup) (REFERENCE_TIME* prt, BSTR* ppName) = 0;
+	STDMETHOD(ChapSort) () = 0;
+};
+
+class CDSMChapter
+{
+	static int counter;
+	int order;
+
+public:
+	REFERENCE_TIME rt;
+	CStringW name;
+	CDSMChapter();
+	CDSMChapter(REFERENCE_TIME rt, LPCWSTR name);
+	void operator = (const CDSMChapter& c);
+	static int Compare(const void* a, const void* b);
+};
+
+class IDSMChapterBagImpl : public IDSMChapterBag
+{
+protected:
+	CAtlArray<CDSMChapter> m_chapters;
+	bool m_fSorted;
+
+public:
+	IDSMChapterBagImpl();
+
+	void operator += (const CDSMChapter& c) {m_chapters.Add(c); m_fSorted = false;}
+
+	// IDSMChapterBag
+
+	STDMETHODIMP_(DWORD) ChapGetCount();
+	STDMETHODIMP ChapGet(DWORD iIndex, REFERENCE_TIME* prt, BSTR* ppName = NULL);
+	STDMETHODIMP ChapSet(DWORD iIndex, REFERENCE_TIME rt, LPCWSTR pName);
+	STDMETHODIMP ChapAppend(REFERENCE_TIME rt, LPCWSTR pName);
+	STDMETHODIMP ChapRemoveAt(DWORD iIndex);
+	STDMETHODIMP ChapRemoveAll();
+	STDMETHODIMP_(long) ChapLookup(REFERENCE_TIME* prt, BSTR* ppName = NULL);
+	STDMETHODIMP ChapSort();
+};
+
+class CDSMChapterBag : public CUnknown, public IDSMChapterBagImpl
+{
+public:
+	CDSMChapterBag(LPUNKNOWN pUnk, HRESULT* phr);
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+};
+
+template<class T>
+int range_bsearch(const CAtlArray<T>& array, REFERENCE_TIME rt)
+{
+	int i = 0, j = array.GetCount() - 1, ret = -1;
+	if(j >= 0 && rt >= array[j].rt) return j;
+	while(i < j)
+	{
+		int mid = (i + j) >> 1;
+		REFERENCE_TIME midrt = array[mid].rt;
+		if(rt == midrt) {ret = mid; break;}
+		else if(rt < midrt) {ret = -1; if(j == mid) mid--; j = mid;}
+		else if(rt > midrt) {ret = mid; if(i == mid) mid++; i = mid;}
+	}
+	return ret;
+}
diff --git a/vsfilter/dsutil/DSUtil.cpp b/vsfilter/dsutil/DSUtil.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..650d242f8b8d7df9943e4edb2181477d6a6eb6d6
--- /dev/null
+++ b/vsfilter/dsutil/DSUtil.cpp
@@ -0,0 +1,2218 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <Vfw.h>
+#include "../include/winddk/devioctl.h"
+#include "../include/winddk/ntddcdrm.h"
+#include "DSUtil.h"
+#include "../include/moreuuids.h"
+
+void DumpStreamConfig(TCHAR* fn, IAMStreamConfig* pAMVSCCap)
+{
+	CString s, ss;
+	CStdioFile f;
+	if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeText))
+		return;
+
+	int cnt = 0, size = 0;
+	if(FAILED(pAMVSCCap->GetNumberOfCapabilities(&cnt, &size)))
+		return;
+
+	s.Empty();
+	s.Format(_T("cnt %d, size %d\n"), cnt, size);
+	f.WriteString(s);
+
+	if(size == sizeof(VIDEO_STREAM_CONFIG_CAPS))
+	{
+		for(int i = 0; i < cnt; i++)
+		{
+			AM_MEDIA_TYPE* pmt = NULL;
+
+			VIDEO_STREAM_CONFIG_CAPS caps;
+			memset(&caps, 0, sizeof(caps));
+
+			s.Empty();
+			ss.Format(_T("%d\n"), i); s += ss;
+			f.WriteString(s);
+
+			if(FAILED(pAMVSCCap->GetStreamCaps(i, &pmt, (BYTE*)&caps)))
+				continue;
+
+			{
+				s.Empty();
+				ss.Format(_T("VIDEO_STREAM_CONFIG_CAPS\n")); s += ss;
+				ss.Format(_T("\tVideoStandard 0x%08x\n"), caps.VideoStandard); s += ss;
+				ss.Format(_T("\tInputSize %dx%d\n"), caps.InputSize); s += ss;
+				ss.Format(_T("\tCroppingSize %dx%d - %dx%d\n"), caps.MinCroppingSize, caps.MaxCroppingSize); s += ss;
+				ss.Format(_T("\tCropGranularity %d, %d\n"), caps.CropGranularityX, caps.CropGranularityY); s += ss;
+				ss.Format(_T("\tCropAlign %d, %d\n"), caps.CropAlignX, caps.CropAlignY); s += ss;
+				ss.Format(_T("\tOutputSize %dx%d - %dx%d\n"), caps.MinOutputSize, caps.MaxOutputSize); s += ss;
+				ss.Format(_T("\tOutputGranularity %d, %d\n"), caps.OutputGranularityX, caps.OutputGranularityY); s += ss;
+				ss.Format(_T("\tStretchTaps %d, %d\n"), caps.StretchTapsX, caps.StretchTapsY); s += ss;
+				ss.Format(_T("\tShrinkTaps %d, %d\n"), caps.ShrinkTapsX, caps.ShrinkTapsY); s += ss;
+				ss.Format(_T("\tFrameInterval %I64d, %I64d (%.4f, %.4f)\n"), 
+					caps.MinFrameInterval, caps.MaxFrameInterval,
+					(float)10000000/caps.MinFrameInterval, (float)10000000/caps.MaxFrameInterval); s += ss;
+				ss.Format(_T("\tBitsPerSecond %d - %d\n"), caps.MinBitsPerSecond, caps.MaxBitsPerSecond); s += ss;
+				f.WriteString(s);
+			}
+
+			BITMAPINFOHEADER* pbh;
+			if(pmt->formattype == FORMAT_VideoInfo) 
+			{
+				VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
+				pbh = &vih->bmiHeader;
+
+				s.Empty();
+				ss.Format(_T("FORMAT_VideoInfo\n")); s += ss;
+				ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
+				ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
+				ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
+				f.WriteString(s);
+			}
+			else if(pmt->formattype == FORMAT_VideoInfo2) 
+			{
+				VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
+				pbh = &vih->bmiHeader;
+
+				s.Empty();
+				ss.Format(_T("FORMAT_VideoInfo2\n")); s += ss;
+				ss.Format(_T("\tAvgTimePerFrame %I64d, %.4f\n"), vih->AvgTimePerFrame, (float)10000000/vih->AvgTimePerFrame); s += ss;
+				ss.Format(_T("\trcSource %d,%d,%d,%d\n"), vih->rcSource); s += ss;
+				ss.Format(_T("\trcTarget %d,%d,%d,%d\n"), vih->rcTarget); s += ss;
+				ss.Format(_T("\tdwInterlaceFlags 0x%x\n"), vih->dwInterlaceFlags); s += ss;
+				ss.Format(_T("\tdwPictAspectRatio %d:%d\n"), vih->dwPictAspectRatioX, vih->dwPictAspectRatioY); s += ss;
+				f.WriteString(s);
+			}
+			else
+			{
+				DeleteMediaType(pmt);
+				continue;
+			}
+
+			s.Empty();
+			ss.Format(_T("BITMAPINFOHEADER\n")); s += ss;
+			ss.Format(_T("\tbiCompression %x\n"), pbh->biCompression); s += ss;
+			ss.Format(_T("\tbiWidth %d\n"), pbh->biWidth); s += ss;
+			ss.Format(_T("\tbiHeight %d\n"), pbh->biHeight); s += ss;
+			ss.Format(_T("\tbiBitCount %d\n"), pbh->biBitCount); s += ss;
+			ss.Format(_T("\tbiPlanes %d\n"), pbh->biPlanes); s += ss;
+			ss.Format(_T("\tbiSizeImage %d\n"), pbh->biSizeImage); s += ss;
+			f.WriteString(s);
+
+			DeleteMediaType(pmt);
+		}
+	}
+	else if(size == sizeof(AUDIO_STREAM_CONFIG_CAPS))
+	{
+		// TODO
+	}
+}
+
+int CountPins(IBaseFilter* pBF, int& nIn, int& nOut, int& nInC, int& nOutC)
+{
+	nIn = nOut = 0;
+	nInC = nOutC = 0;
+
+	BeginEnumPins(pBF, pEP, pPin)
+	{
+		PIN_DIRECTION dir;
+		if(SUCCEEDED(pPin->QueryDirection(&dir)))
+		{
+			CComPtr<IPin> pPinConnectedTo;
+			pPin->ConnectedTo(&pPinConnectedTo);
+
+			if(dir == PINDIR_INPUT) {nIn++; if(pPinConnectedTo) nInC++;}
+			else if(dir == PINDIR_OUTPUT) {nOut++; if(pPinConnectedTo) nOutC++;}
+		}
+	}
+	EndEnumPins
+
+	return(nIn+nOut);
+}
+
+bool IsSplitter(IBaseFilter* pBF, bool fCountConnectedOnly)
+{
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+	return(fCountConnectedOnly ? nOutC > 1 : nOut > 1);
+}
+
+bool IsMultiplexer(IBaseFilter* pBF, bool fCountConnectedOnly)
+{
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+	return(fCountConnectedOnly ? nInC > 1 : nIn > 1);
+}
+
+bool IsStreamStart(IBaseFilter* pBF)
+{
+	CComQIPtr<IAMFilterMiscFlags> pAMMF(pBF);
+	if(pAMMF && pAMMF->GetMiscFlags()&AM_FILTER_MISC_FLAGS_IS_SOURCE)
+		return(true);
+
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+	AM_MEDIA_TYPE mt;
+	CComPtr<IPin> pIn = GetFirstPin(pBF);
+	return((nOut > 1)
+		|| (nOut > 0 && nIn == 1 && pIn && SUCCEEDED(pIn->ConnectionMediaType(&mt)) && mt.majortype == MEDIATYPE_Stream));
+}
+
+bool IsStreamEnd(IBaseFilter* pBF)
+{
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+	return(nOut == 0);
+}
+
+bool IsVideoRenderer(IBaseFilter* pBF)
+{
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+
+	if(nInC > 0 && nOut == 0)
+	{
+		BeginEnumPins(pBF, pEP, pPin)
+		{
+			AM_MEDIA_TYPE mt;
+			if(S_OK != pPin->ConnectionMediaType(&mt))
+				continue;
+
+			FreeMediaType(mt);
+
+			return(!!(mt.majortype == MEDIATYPE_Video));
+				/*&& (mt.formattype == FORMAT_VideoInfo || mt.formattype == FORMAT_VideoInfo2));*/
+		}
+		EndEnumPins
+	}
+
+	CLSID clsid;
+	memcpy(&clsid, &GUID_NULL, sizeof(clsid));
+	pBF->GetClassID(&clsid);
+
+	return(clsid == CLSID_VideoRenderer || clsid == CLSID_VideoRendererDefault);
+}
+
+#include <initguid.h>
+
+DEFINE_GUID(CLSID_ReClock, 
+0x9dc15360, 0x914c, 0x46b8, 0xb9, 0xdf, 0xbf, 0xe6, 0x7f, 0xd3, 0x6c, 0x6a); 
+
+bool IsAudioWaveRenderer(IBaseFilter* pBF)
+{
+	int nIn, nOut, nInC, nOutC;
+	CountPins(pBF, nIn, nOut, nInC, nOutC);
+
+	if(nInC > 0 && nOut == 0 && CComQIPtr<IBasicAudio>(pBF))
+	{
+		BeginEnumPins(pBF, pEP, pPin)
+		{
+			AM_MEDIA_TYPE mt;
+			if(S_OK != pPin->ConnectionMediaType(&mt))
+				continue;
+
+			FreeMediaType(mt);
+
+			return(!!(mt.majortype == MEDIATYPE_Audio)
+				/*&& mt.formattype == FORMAT_WaveFormatEx*/);
+		}
+		EndEnumPins
+	}
+
+	CLSID clsid;
+	memcpy(&clsid, &GUID_NULL, sizeof(clsid));
+	pBF->GetClassID(&clsid);
+
+	return(clsid == CLSID_DSoundRender || clsid == CLSID_AudioRender || clsid == CLSID_ReClock
+		|| clsid == __uuidof(CNullAudioRenderer) || clsid == __uuidof(CNullUAudioRenderer));
+}
+
+IBaseFilter* GetUpStreamFilter(IBaseFilter* pBF, IPin* pInputPin)
+{
+	return GetFilterFromPin(GetUpStreamPin(pBF, pInputPin));
+}
+
+IPin* GetUpStreamPin(IBaseFilter* pBF, IPin* pInputPin)
+{
+	BeginEnumPins(pBF, pEP, pPin)
+	{
+		if(pInputPin && pInputPin != pPin) continue;
+
+		PIN_DIRECTION dir;
+		CComPtr<IPin> pPinConnectedTo;
+		if(SUCCEEDED(pPin->QueryDirection(&dir)) && dir == PINDIR_INPUT
+		&& SUCCEEDED(pPin->ConnectedTo(&pPinConnectedTo)))
+		{
+			IPin* pRet = pPinConnectedTo.Detach();
+			pRet->Release();
+			return(pRet);
+		}
+	}
+	EndEnumPins
+
+	return(NULL);
+}
+
+IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir)
+{
+	if(!pBF) return(NULL);
+
+	BeginEnumPins(pBF, pEP, pPin)
+	{
+		PIN_DIRECTION dir2;
+		pPin->QueryDirection(&dir2);
+		if(dir == dir2)
+		{
+			IPin* pRet = pPin.Detach();
+			pRet->Release();
+			return(pRet);
+		}
+	}
+	EndEnumPins
+
+	return(NULL);
+}
+
+IPin* GetFirstDisconnectedPin(IBaseFilter* pBF, PIN_DIRECTION dir)
+{
+	if(!pBF) return(NULL);
+
+	BeginEnumPins(pBF, pEP, pPin)
+	{
+		PIN_DIRECTION dir2;
+		pPin->QueryDirection(&dir2);
+		CComPtr<IPin> pPinTo;
+		if(dir == dir2 && (S_OK != pPin->ConnectedTo(&pPinTo)))
+		{
+			IPin* pRet = pPin.Detach();
+			pRet->Release();
+			return(pRet);
+		}
+	}
+	EndEnumPins
+
+	return(NULL);
+}
+
+IBaseFilter* FindFilter(LPCWSTR clsid, IFilterGraph* pFG)
+{
+	CLSID clsid2;
+	CLSIDFromString(CComBSTR(clsid), &clsid2);
+	return(FindFilter(clsid2, pFG));
+}
+
+IBaseFilter* FindFilter(const CLSID& clsid, IFilterGraph* pFG)
+{
+	BeginEnumFilters(pFG, pEF, pBF)
+	{
+		CLSID clsid2;
+		if(SUCCEEDED(pBF->GetClassID(&clsid2)) && clsid == clsid2)
+			return(pBF);
+	}
+	EndEnumFilters
+
+	return NULL;
+}
+
+CStringW GetFilterName(IBaseFilter* pBF)
+{
+	CStringW name;
+	CFilterInfo fi;
+	if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
+		name = fi.achName;
+	return(name);
+}
+
+CStringW GetPinName(IPin* pPin)
+{
+	CStringW name;
+	CPinInfo pi;
+	if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi))) 
+		name = pi.achName;
+	return(name);
+}
+
+IFilterGraph* GetGraphFromFilter(IBaseFilter* pBF)
+{
+	if(!pBF) return NULL;
+	IFilterGraph* pGraph = NULL;
+	CFilterInfo fi;
+	if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
+		pGraph = fi.pGraph;
+	return(pGraph);
+}
+
+IBaseFilter* GetFilterFromPin(IPin* pPin)
+{
+	if(!pPin) return NULL;
+	IBaseFilter* pBF = NULL;
+	CPinInfo pi;
+	if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
+		pBF = pi.pFilter;
+	return(pBF);
+}
+
+IPin* AppendFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
+{
+	IPin* pRet = pPin;
+
+	CInterfaceList<IBaseFilter> pFilters;
+
+	do
+	{
+		if(!pPin || DisplayName.IsEmpty() || !pGB)
+			break;
+
+		CComPtr<IPin> pPinTo;
+		PIN_DIRECTION dir;
+		if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
+			break;
+
+		CComPtr<IBindCtx> pBindCtx;
+		CreateBindCtx(0, &pBindCtx);
+
+		CComPtr<IMoniker> pMoniker;
+		ULONG chEaten;
+		if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
+			break;
+
+		CComPtr<IBaseFilter> pBF;
+		if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
+			break;
+
+		CComPtr<IPropertyBag> pPB;
+		if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
+			break;
+
+		CComVariant var;
+		if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
+			break;
+
+		pFilters.AddTail(pBF);
+		BeginEnumFilters(pGB, pEnum, pBF2) 
+			pFilters.AddTail(pBF2); 
+		EndEnumFilters
+
+		if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
+			break;
+
+		BeginEnumFilters(pGB, pEnum, pBF2) 
+			if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2))) 
+				pEnum->Reset();
+		EndEnumFilters
+
+		pPinTo = GetFirstPin(pBF, PINDIR_INPUT);
+		if(!pPinTo)
+		{
+			pGB->RemoveFilter(pBF);
+			break;
+		}
+
+		HRESULT hr;
+		if(FAILED(hr = pGB->ConnectDirect(pPin, pPinTo, NULL)))
+		{
+			hr = pGB->Connect(pPin, pPinTo);
+			pGB->RemoveFilter(pBF);
+			break;
+		}
+
+		BeginEnumFilters(pGB, pEnum, pBF2) 
+			if(!pFilters.Find(pBF2) && SUCCEEDED(pGB->RemoveFilter(pBF2))) 
+				pEnum->Reset();
+		EndEnumFilters
+
+		pRet = GetFirstPin(pBF, PINDIR_OUTPUT);
+		if(!pRet)
+		{
+			pRet = pPin;
+			pGB->RemoveFilter(pBF);
+			break;
+		}
+	}
+	while(false);
+
+	return(pRet);
+}
+
+IPin* InsertFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB)
+{
+	do
+	{
+		if(!pPin || DisplayName.IsEmpty() || !pGB)
+			break;
+
+		PIN_DIRECTION dir;
+		if(FAILED(pPin->QueryDirection(&dir)))
+			break;
+
+		CComPtr<IPin> pFrom, pTo;
+
+		if(dir == PINDIR_INPUT)
+		{
+			pPin->ConnectedTo(&pFrom);
+			pTo = pPin;
+		}
+		else if(dir == PINDIR_OUTPUT)
+		{
+			pFrom = pPin;
+			pPin->ConnectedTo(&pTo);
+		}
+		
+		if(!pFrom || !pTo)
+			break;
+
+		CComPtr<IBindCtx> pBindCtx;
+		CreateBindCtx(0, &pBindCtx);
+
+		CComPtr<IMoniker> pMoniker;
+		ULONG chEaten;
+		if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
+			break;
+
+		CComPtr<IBaseFilter> pBF;
+		if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
+			break;
+
+		CComPtr<IPropertyBag> pPB;
+		if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
+			break;
+
+		CComVariant var;
+		if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
+			break;
+
+		if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
+			break;
+
+		CComPtr<IPin> pFromTo = GetFirstPin(pBF, PINDIR_INPUT);
+		if(!pFromTo)
+		{
+			pGB->RemoveFilter(pBF);
+			break;
+		}
+
+		if(FAILED(pGB->Disconnect(pFrom)) || FAILED(pGB->Disconnect(pTo)))
+		{
+			pGB->RemoveFilter(pBF);
+			pGB->ConnectDirect(pFrom, pTo, NULL);
+			break;
+		}
+
+		HRESULT hr;
+		if(FAILED(hr = pGB->ConnectDirect(pFrom, pFromTo, NULL)))
+		{
+			pGB->RemoveFilter(pBF);
+			pGB->ConnectDirect(pFrom, pTo, NULL);
+			break;
+		}
+
+		CComPtr<IPin> pToFrom = GetFirstPin(pBF, PINDIR_OUTPUT);
+		if(!pToFrom)
+		{
+			pGB->RemoveFilter(pBF);
+			pGB->ConnectDirect(pFrom, pTo, NULL);
+			break;
+		}
+
+		if(FAILED(pGB->ConnectDirect(pToFrom, pTo, NULL)))
+		{
+			pGB->RemoveFilter(pBF);
+			pGB->ConnectDirect(pFrom, pTo, NULL);
+			break;
+		}
+
+		pPin = pToFrom;
+	}
+	while(false);
+
+	return(pPin);
+}
+
+void ExtractMediaTypes(IPin* pPin, CAtlArray<GUID>& types)
+{
+	types.RemoveAll();
+
+    BeginEnumMediaTypes(pPin, pEM, pmt)
+	{
+		bool fFound = false;
+
+		for(int i = 0; !fFound && i < types.GetCount(); i += 2)
+		{
+			if(types[i] == pmt->majortype && types[i+1] == pmt->subtype)
+				fFound = true;
+		}
+
+		if(!fFound)
+		{
+			types.Add(pmt->majortype);
+			types.Add(pmt->subtype);
+		}
+	}
+	EndEnumMediaTypes(pmt)
+}
+
+void ExtractMediaTypes(IPin* pPin, CAtlList<CMediaType>& mts)
+{
+	mts.RemoveAll();
+
+    BeginEnumMediaTypes(pPin, pEM, pmt)
+	{
+		bool fFound = false;
+
+		POSITION pos = mts.GetHeadPosition();
+		while(!fFound && pos)
+		{
+			CMediaType& mt = mts.GetNext(pos);
+			if(mt.majortype == pmt->majortype && mt.subtype == pmt->subtype)
+				fFound = true;
+		}
+
+		if(!fFound)
+		{
+			mts.AddTail(CMediaType(*pmt));
+		}
+	}
+	EndEnumMediaTypes(pmt)
+}
+
+int Eval_Exception(int n_except)
+{
+    if(n_except == STATUS_ACCESS_VIOLATION)
+	{
+		AfxMessageBox(_T("The property page of this filter has just caused a\nmemory access violation. The application will gently die now :)"));
+	}
+
+	return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void MyOleCreatePropertyFrame(HWND hwndOwner, UINT x, UINT y, LPCOLESTR lpszCaption, ULONG cObjects, LPUNKNOWN FAR* lplpUnk, ULONG cPages, LPCLSID lpPageClsID, LCID lcid, DWORD dwReserved, LPVOID lpvReserved)
+{
+	__try
+	{
+		OleCreatePropertyFrame(hwndOwner, x, y, lpszCaption, cObjects, lplpUnk, cPages, lpPageClsID, lcid, dwReserved, lpvReserved);
+	}
+	__except(Eval_Exception(GetExceptionCode()))
+	{
+		// No code; this block never executed.
+	}
+}
+
+void ShowPPage(CString DisplayName, HWND hParentWnd)
+{
+	CComPtr<IBindCtx> pBindCtx;
+	CreateBindCtx(0, &pBindCtx);
+
+	CComPtr<IMoniker> pMoniker;
+	ULONG chEaten;
+	if(S_OK != MkParseDisplayName(pBindCtx, CStringW(DisplayName), &chEaten, &pMoniker))
+		return;
+
+	CComPtr<IBaseFilter> pBF;
+	if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
+		return;
+
+	ShowPPage(pBF, hParentWnd);	
+}
+
+void ShowPPage(IUnknown* pUnk, HWND hParentWnd)
+{
+	CComQIPtr<ISpecifyPropertyPages> pSPP = pUnk;
+	if(!pSPP) return;
+
+	CString str;
+
+	CComQIPtr<IBaseFilter> pBF = pSPP;
+	CFilterInfo fi;
+	CComQIPtr<IPin> pPin = pSPP;
+	CPinInfo pi;
+	if(pBF && SUCCEEDED(pBF->QueryFilterInfo(&fi)))
+		str = fi.achName;
+	else if(pPin && SUCCEEDED(pPin->QueryPinInfo(&pi)))
+		str = pi.achName;
+
+	CAUUID caGUID;
+	caGUID.pElems = NULL;
+	if(SUCCEEDED(pSPP->GetPages(&caGUID)))
+	{
+		IUnknown* lpUnk = NULL;
+		pSPP.QueryInterface(&lpUnk);
+		MyOleCreatePropertyFrame(
+			hParentWnd, 0, 0, CStringW(str), 
+			1, (IUnknown**)&lpUnk, 
+			caGUID.cElems, caGUID.pElems, 
+			0, 0, NULL);
+		lpUnk->Release();
+
+		if(caGUID.pElems) CoTaskMemFree(caGUID.pElems);
+	}
+}
+
+CLSID GetCLSID(IBaseFilter* pBF)
+{
+	CLSID clsid = GUID_NULL;
+	if(pBF) pBF->GetClassID(&clsid);
+	return(clsid);
+}
+
+CLSID GetCLSID(IPin* pPin)
+{
+	return(GetCLSID(GetFilterFromPin(pPin)));
+}
+
+bool IsCLSIDRegistered(LPCTSTR clsid)
+{
+	CString rootkey1(_T("CLSID\\"));
+	CString rootkey2(_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\"));
+
+	return ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey1 + clsid, KEY_READ)
+		|| ERROR_SUCCESS == CRegKey().Open(HKEY_CLASSES_ROOT, rootkey2 + clsid, KEY_READ);
+}
+
+bool IsCLSIDRegistered(const CLSID& clsid)
+{
+	bool fRet = false;
+
+	LPOLESTR pStr = NULL;
+	if(S_OK == StringFromCLSID(clsid, &pStr) && pStr)
+	{
+		fRet = IsCLSIDRegistered(CString(pStr));
+		CoTaskMemFree(pStr);
+	}
+
+	return(fRet);
+}
+
+void CStringToBin(CString str, CAtlArray<BYTE>& data)
+{
+	str.Trim();
+	ASSERT((str.GetLength()&1) == 0);
+	data.SetCount(str.GetLength()/2);
+
+	BYTE b = 0;
+
+	str.MakeUpper();
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+	{
+		TCHAR c = str[i];
+		if(c >= '0' && c <= '9') 
+		{
+			if(!(i&1)) b = ((char(c-'0')<<4)&0xf0)|(b&0x0f);
+			else b = (char(c-'0')&0x0f)|(b&0xf0);
+		}
+		else if(c >= 'A' && c <= 'F')
+		{
+			if(!(i&1)) b = ((char(c-'A'+10)<<4)&0xf0)|(b&0x0f);
+			else b = (char(c-'A'+10)&0x0f)|(b&0xf0);
+		}
+		else break;
+
+		if(i&1)
+		{
+			data[i>>1] = b;
+			b = 0;
+		}
+	}
+}
+
+CString BinToCString(BYTE* ptr, int len)
+{
+	CString ret;
+
+	while(len-- > 0)
+	{
+		TCHAR high, low;
+		high = (*ptr>>4) >= 10 ? (*ptr>>4)-10 + 'A' : (*ptr>>4) + '0';
+		low = (*ptr&0xf) >= 10 ? (*ptr&0xf)-10 + 'A' : (*ptr&0xf) + '0';
+
+		CString str;
+		str.Format(_T("%c%c"), high, low);
+		ret += str;
+
+		ptr++;
+	}
+
+	return(ret);
+}
+
+static void FindFiles(CString fn, CAtlList<CString>& files)
+{
+	CString path = fn;
+	path.Replace('/', '\\');
+	path = path.Left(path.ReverseFind('\\')+1);
+
+	WIN32_FIND_DATA findData;
+	HANDLE h = FindFirstFile(fn, &findData);
+	if(h != INVALID_HANDLE_VALUE)
+	{
+		do {files.AddTail(path + findData.cFileName);}
+		while(FindNextFile(h, &findData));
+
+		FindClose(h);
+	}
+}
+
+cdrom_t GetCDROMType(TCHAR drive, CAtlList<CString>& files)
+{
+	files.RemoveAll();
+
+	CString path;
+	path.Format(_T("%c:"), drive);
+
+	if(GetDriveType(path + _T("\\")) == DRIVE_CDROM)
+	{
+		// CDROM_VideoCD
+		FindFiles(path + _T("\\mpegav\\avseq??.dat"), files);
+		FindFiles(path + _T("\\mpegav\\avseq??.mpg"), files);
+		FindFiles(path + _T("\\mpeg2\\avseq??.dat"), files);
+		FindFiles(path + _T("\\mpeg2\\avseq??.mpg"), files);
+		FindFiles(path + _T("\\mpegav\\music??.dat"), files);
+		FindFiles(path + _T("\\mpegav\\music??.mpg"), files);
+		FindFiles(path + _T("\\mpeg2\\music??.dat"), files);
+		FindFiles(path + _T("\\mpeg2\\music??.mpg"), files);
+		if(files.GetCount() > 0) return CDROM_VideoCD;
+
+		// CDROM_DVDVideo
+		FindFiles(path + _T("\\VIDEO_TS\\video_ts.ifo"), files);
+		if(files.GetCount() > 0) return CDROM_DVDVideo;
+
+		// CDROM_Audio
+		if(!(GetVersion()&0x80000000))
+		{
+			HANDLE hDrive = CreateFile(CString(_T("\\\\.\\")) + path, GENERIC_READ, FILE_SHARE_READ, NULL, 
+				OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
+			if(hDrive != INVALID_HANDLE_VALUE)
+			{
+				DWORD BytesReturned;
+				CDROM_TOC TOC;
+				if(DeviceIoControl(hDrive, IOCTL_CDROM_READ_TOC, NULL, 0, &TOC, sizeof(TOC), &BytesReturned, 0))
+				{
+					for(int i = TOC.FirstTrack; i <= TOC.LastTrack; i++)
+					{
+						// MMC-3 Draft Revision 10g: Table 222 – Q Sub-channel control field
+						TOC.TrackData[i-1].Control &= 5;
+						if(TOC.TrackData[i-1].Control == 0 || TOC.TrackData[i-1].Control == 1) 
+						{
+							CString fn;
+							fn.Format(_T("%s\\track%02d.cda"), path, i);
+							files.AddTail(fn);
+						}
+					}
+				}
+
+				CloseHandle(hDrive);
+			}
+		}
+		if(files.GetCount() > 0) return CDROM_Audio;
+
+		// it is a cdrom but nothing special
+		return CDROM_Unknown;
+	}
+	
+	return CDROM_NotFound;
+}
+
+CString GetDriveLabel(TCHAR drive)
+{
+	CString label;
+
+	CString path;
+	path.Format(_T("%c:\\"), drive);
+	TCHAR VolumeNameBuffer[MAX_PATH], FileSystemNameBuffer[MAX_PATH];
+	DWORD VolumeSerialNumber, MaximumComponentLength, FileSystemFlags;
+	if(GetVolumeInformation(path, 
+		VolumeNameBuffer, MAX_PATH, &VolumeSerialNumber, &MaximumComponentLength, 
+		&FileSystemFlags, FileSystemNameBuffer, MAX_PATH))
+	{
+		label = VolumeNameBuffer;
+	}
+
+	return(label);
+}
+
+bool GetKeyFrames(CString fn, CUIntArray& kfs)
+{
+	kfs.RemoveAll();
+
+	CString fn2 = CString(fn).MakeLower();
+	if(fn2.Mid(fn2.ReverseFind('.')+1) == _T("avi"))
+	{
+		AVIFileInit();
+	    
+		PAVIFILE pfile;
+		if(AVIFileOpen(&pfile, fn, OF_SHARE_DENY_WRITE, 0L) == 0)
+		{
+			AVIFILEINFO afi;
+			memset(&afi, 0, sizeof(afi));
+			AVIFileInfo(pfile, &afi, sizeof(AVIFILEINFO));
+
+			CComPtr<IAVIStream> pavi;
+			if(AVIFileGetStream(pfile, &pavi, streamtypeVIDEO, 0) == AVIERR_OK)
+			{
+				AVISTREAMINFO si;
+				AVIStreamInfo(pavi, &si, sizeof(si));
+
+				if(afi.dwCaps&AVIFILECAPS_ALLKEYFRAMES)
+				{
+					kfs.SetSize(si.dwLength);
+					for(int kf = 0; kf < si.dwLength; kf++) kfs[kf] = kf;
+				}
+				else
+				{
+					for(int kf = 0; ; kf++)
+					{
+						kf = pavi->FindSample(kf, FIND_KEY|FIND_NEXT);
+						if(kf < 0 || kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] >= kf) break;
+						kfs.Add(kf);
+					}
+
+					if(kfs.GetCount() > 0 && kfs[kfs.GetCount()-1] < si.dwLength-1)
+					{
+						kfs.Add(si.dwLength-1);
+					}
+				}
+			}
+
+			AVIFileRelease(pfile);
+		}
+
+		AVIFileExit();
+	}
+
+	return(kfs.GetCount() > 0);
+}
+
+DVD_HMSF_TIMECODE RT2HMSF(REFERENCE_TIME rt, double fps)
+{
+	DVD_HMSF_TIMECODE hmsf = 
+	{
+		(BYTE)((rt/10000000/60/60)),
+		(BYTE)((rt/10000000/60)%60),
+		(BYTE)((rt/10000000)%60),
+		(BYTE)(1.0*((rt/10000)%1000) * fps / 1000)
+	};
+
+	return hmsf;
+}
+
+REFERENCE_TIME HMSF2RT(DVD_HMSF_TIMECODE hmsf, double fps)
+{
+	if(fps == 0) {hmsf.bFrames = 0; fps = 1;}
+	return (REFERENCE_TIME)((((REFERENCE_TIME)hmsf.bHours*60+hmsf.bMinutes)*60+hmsf.bSeconds)*1000+1.0*hmsf.bFrames*1000/fps)*10000;
+}
+
+void memsetd(void* dst, unsigned int c, int nbytes)
+{
+	__asm
+	{
+		mov eax, c
+		mov ecx, nbytes
+		shr ecx, 2
+		mov edi, dst
+		cld
+		rep stosd
+	}
+}
+
+bool ExtractBIH(const AM_MEDIA_TYPE* pmt, BITMAPINFOHEADER* bih)
+{
+	if(pmt && bih)
+	{
+		memset(bih, 0, sizeof(*bih));
+
+		if(pmt->formattype == FORMAT_VideoInfo)
+		{
+			VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
+			memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
+			return true;
+		}
+		else if(pmt->formattype == FORMAT_VideoInfo2)
+		{
+			VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
+			memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
+			return true;
+		}
+		else if(pmt->formattype == FORMAT_MPEGVideo)
+		{
+			VIDEOINFOHEADER* vih = &((MPEG1VIDEOINFO*)pmt->pbFormat)->hdr;
+			memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
+			return true;
+		}
+		else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
+		{
+			VIDEOINFOHEADER2* vih = &((MPEG2VIDEOINFO*)pmt->pbFormat)->hdr;
+			memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
+			return true;
+		}
+		else if(pmt->formattype == FORMAT_DiracVideoInfo)
+		{
+			VIDEOINFOHEADER2* vih = &((DIRACINFOHEADER*)pmt->pbFormat)->hdr;
+			memcpy(bih, &vih->bmiHeader, sizeof(BITMAPINFOHEADER));
+			return true;
+		}
+	}
+	
+	return false;
+}
+
+bool ExtractBIH(IMediaSample* pMS, BITMAPINFOHEADER* bih)
+{
+	AM_MEDIA_TYPE* pmt = NULL;
+	pMS->GetMediaType(&pmt);
+	if(pmt)
+	{
+		bool fRet = ExtractBIH(pmt, bih);
+		DeleteMediaType(pmt);
+		return(fRet);
+	}
+	
+	return(false);
+}
+
+bool ExtractDim(const AM_MEDIA_TYPE* pmt, int& w, int& h, int& arx, int& ary)
+{
+	w = h = arx = ary = 0;
+
+	if(pmt->formattype == FORMAT_VideoInfo || pmt->formattype == FORMAT_MPEGVideo)
+	{
+		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pmt->pbFormat;
+		w = vih->bmiHeader.biWidth;
+		h = abs(vih->bmiHeader.biHeight);
+		arx = w * vih->bmiHeader.biYPelsPerMeter;
+		ary = h * vih->bmiHeader.biXPelsPerMeter;
+	}
+	else if(pmt->formattype == FORMAT_VideoInfo2 || pmt->formattype == FORMAT_MPEG2_VIDEO || pmt->formattype == FORMAT_DiracVideoInfo)
+	{
+		VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pmt->pbFormat;
+		w = vih->bmiHeader.biWidth;
+		h = abs(vih->bmiHeader.biHeight);
+		arx = vih->dwPictAspectRatioX;
+		ary = vih->dwPictAspectRatioY;
+	}
+	else
+	{
+		return(false);
+	}
+
+	if(!arx || !ary)
+	{
+		arx = w;
+		ary = h;
+	}
+
+	BYTE* ptr = NULL;
+	DWORD len = 0;
+
+	if(pmt->formattype == FORMAT_MPEGVideo)
+	{
+		ptr = ((MPEG1VIDEOINFO*)pmt->pbFormat)->bSequenceHeader;
+		len = ((MPEG1VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
+
+		if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
+		{
+			w = (ptr[4]<<4)|(ptr[5]>>4);
+			h = ((ptr[5]&0xf)<<8)|ptr[6];
+			float ar[] = 
+			{
+				1.0000f,1.0000f,0.6735f,0.7031f,
+				0.7615f,0.8055f,0.8437f,0.8935f,
+				0.9157f,0.9815f,1.0255f,1.0695f,
+				1.0950f,1.1575f,1.2015f,1.0000f,
+			};
+			arx = (int)((float)w / ar[ptr[7]>>4] + 0.5);
+			ary = h;
+		}
+	}
+	else if(pmt->formattype == FORMAT_MPEG2_VIDEO)
+	{
+		ptr = (BYTE*)((MPEG2VIDEOINFO*)pmt->pbFormat)->dwSequenceHeader; 
+		len = ((MPEG2VIDEOINFO*)pmt->pbFormat)->cbSequenceHeader;
+
+		if(ptr && len >= 8 && *(DWORD*)ptr == 0xb3010000)
+		{
+			w = (ptr[4]<<4)|(ptr[5]>>4);
+			h = ((ptr[5]&0xf)<<8)|ptr[6];
+			struct {int x, y;} ar[] = {{w,h},{4,3},{16,9},{221,100},{w,h}};
+			int i = min(max(ptr[7]>>4, 1), 5)-1;
+			arx = ar[i].x;
+			ary = ar[i].y;
+		}
+	}
+
+	if(ptr && len >= 8)
+	{
+
+	}
+
+	DWORD a = arx, b = ary;
+    while(a) {int tmp = a; a = b % tmp; b = tmp;}
+	if(b) arx /= b, ary /= b;
+
+	return(true);
+}
+
+bool MakeMPEG2MediaType(CMediaType& mt, BYTE* seqhdr, DWORD len, int w, int h)
+{
+	if(len < 4 || *(DWORD*)seqhdr != 0xb3010000) return false;
+
+	BYTE* seqhdr_ext = NULL;
+
+	BYTE* seqhdr_end = seqhdr + 11;
+	if(seqhdr_end - seqhdr > len) return false;
+	if(*seqhdr_end & 0x02) seqhdr_end += 64;
+	if(seqhdr_end - seqhdr > len) return false;
+	if(*seqhdr_end & 0x01) seqhdr_end += 64;
+	if(seqhdr_end - seqhdr > len) return false;
+	seqhdr_end++;
+	if(seqhdr_end - seqhdr > len) return false;
+	if(len - (seqhdr_end - seqhdr) > 4 && *(DWORD*)seqhdr_end == 0xb5010000) {seqhdr_ext = seqhdr_end; seqhdr_end += 10;}
+	if(seqhdr_end - seqhdr > len) return false;
+
+	len = seqhdr_end - seqhdr;
+	
+	mt = CMediaType();
+
+	mt.majortype = MEDIATYPE_Video;
+	mt.subtype = MEDIASUBTYPE_MPEG2_VIDEO;
+	mt.formattype = FORMAT_MPEG2Video;
+
+	MPEG2VIDEOINFO* vih = (MPEG2VIDEOINFO*)mt.AllocFormatBuffer(FIELD_OFFSET(MPEG2VIDEOINFO, dwSequenceHeader) + len);
+	memset(mt.Format(), 0, mt.FormatLength());
+	vih->hdr.bmiHeader.biSize = sizeof(vih->hdr.bmiHeader);
+	vih->hdr.bmiHeader.biWidth = w;
+	vih->hdr.bmiHeader.biHeight = h;
+
+	BYTE* pSequenceHeader = (BYTE*)vih->dwSequenceHeader;
+	memcpy(pSequenceHeader, seqhdr, len);
+	vih->cbSequenceHeader = len;
+	
+	static char profile[8] = 
+	{
+		0, AM_MPEG2Profile_High, AM_MPEG2Profile_SpatiallyScalable, AM_MPEG2Profile_SNRScalable, 
+		AM_MPEG2Profile_Main, AM_MPEG2Profile_Simple, 0, 0
+	};
+
+	static char level[16] =
+	{
+		0, 0, 0, 0, 
+		AM_MPEG2Level_High, 0, AM_MPEG2Level_High1440, 0, 
+		AM_MPEG2Level_Main, 0, AM_MPEG2Level_Low, 0, 
+		0, 0, 0, 0
+	};
+
+	if(seqhdr_ext && (seqhdr_ext[4] & 0xf0) == 0x10)
+	{
+		vih->dwProfile = profile[seqhdr_ext[4] & 0x07];
+		vih->dwLevel = level[seqhdr_ext[5] >> 4];
+	}
+
+	return true;
+}
+
+unsigned __int64 GetFileVersion(LPCTSTR fn)
+{
+	unsigned __int64 ret = 0;
+
+	DWORD buff[4];
+	VS_FIXEDFILEINFO* pvsf = (VS_FIXEDFILEINFO*)buff;
+	DWORD d; // a variable that GetFileVersionInfoSize sets to zero (but why is it needed ?????????????????????????????? :)
+	DWORD len = GetFileVersionInfoSize((TCHAR*)fn, &d);
+	
+	if(len)
+	{
+		TCHAR* b1 = new TCHAR[len];
+		if(b1)
+		{
+            UINT uLen;
+            if(GetFileVersionInfo((TCHAR*)fn, 0, len, b1) && VerQueryValue(b1, _T("\\"), (void**)&pvsf, &uLen))
+            {
+				ret = ((unsigned __int64)pvsf->dwFileVersionMS<<32) | pvsf->dwFileVersionLS;
+            }
+
+            delete [] b1;
+		}
+	}
+
+	return ret;
+}
+
+bool CreateFilter(CStringW DisplayName, IBaseFilter** ppBF, CStringW& FriendlyName)
+{
+	if(!ppBF) return(false);
+
+	*ppBF = NULL;
+	FriendlyName.Empty();
+
+	CComPtr<IBindCtx> pBindCtx;
+	CreateBindCtx(0, &pBindCtx);
+
+	CComPtr<IMoniker> pMoniker;
+	ULONG chEaten;
+	if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
+		return(false);
+
+	if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)ppBF)) || !*ppBF)
+		return(false);
+
+	CComPtr<IPropertyBag> pPB;
+	CComVariant var;
+	if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
+	&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
+		FriendlyName = var.bstrVal;
+
+	return(true);
+}
+
+IBaseFilter* AppendFilter(IPin* pPin, IMoniker* pMoniker, IGraphBuilder* pGB)
+{
+	do
+	{
+		if(!pPin || !pMoniker || !pGB)
+			break;
+
+		CComPtr<IPin> pPinTo;
+		PIN_DIRECTION dir;
+		if(FAILED(pPin->QueryDirection(&dir)) || dir != PINDIR_OUTPUT || SUCCEEDED(pPin->ConnectedTo(&pPinTo)))
+			break;
+
+		CComPtr<IBindCtx> pBindCtx;
+		CreateBindCtx(0, &pBindCtx);
+
+		CComPtr<IPropertyBag> pPB;
+		if(FAILED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB)))
+			break;
+
+		CComVariant var;
+		if(FAILED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
+			break;
+
+		CComPtr<IBaseFilter> pBF;
+		if(FAILED(pMoniker->BindToObject(pBindCtx, 0, IID_IBaseFilter, (void**)&pBF)) || !pBF)
+			break;
+
+		if(FAILED(pGB->AddFilter(pBF, CStringW(var.bstrVal))))
+			break;
+
+		BeginEnumPins(pBF, pEP, pPinTo)
+		{
+			PIN_DIRECTION dir;
+			if(FAILED(pPinTo->QueryDirection(&dir)) || dir != PINDIR_INPUT)
+				continue;
+
+			if(SUCCEEDED(pGB->ConnectDirect(pPin, pPinTo, NULL)))
+				return(pBF);
+		}
+		EndEnumFilters
+
+		pGB->RemoveFilter(pBF);
+	}
+	while(false);
+
+	return(NULL);
+}
+
+CStringW GetFriendlyName(CStringW DisplayName)
+{
+	CStringW FriendlyName;
+
+	CComPtr<IBindCtx> pBindCtx;
+	CreateBindCtx(0, &pBindCtx);
+
+	CComPtr<IMoniker> pMoniker;
+	ULONG chEaten;
+	if(S_OK != MkParseDisplayName(pBindCtx, CComBSTR(DisplayName), &chEaten, &pMoniker))
+		return(false);
+
+	CComPtr<IPropertyBag> pPB;
+	CComVariant var;
+	if(SUCCEEDED(pMoniker->BindToStorage(pBindCtx, 0, IID_IPropertyBag, (void**)&pPB))
+	&& SUCCEEDED(pPB->Read(CComBSTR(_T("FriendlyName")), &var, NULL)))
+		FriendlyName = var.bstrVal;
+
+	return FriendlyName;
+}
+
+typedef struct
+{
+	CString path;
+	HINSTANCE hInst;
+	CLSID clsid;
+} ExternalObject;
+
+static CAtlList<ExternalObject> s_extobjs;
+
+HRESULT LoadExternalObject(LPCTSTR path, REFCLSID clsid, REFIID iid, void** ppv)
+{
+	CheckPointer(ppv, E_POINTER);
+
+	CString fullpath = MakeFullPath(path);
+
+	HINSTANCE hInst = NULL;
+	bool fFound = false;
+
+	POSITION pos = s_extobjs.GetHeadPosition();
+	while(pos)
+	{
+		ExternalObject& eo = s_extobjs.GetNext(pos);
+		if(!eo.path.CompareNoCase(fullpath))
+		{
+			hInst = eo.hInst;
+			fFound = true;
+			break;
+		}
+	}
+
+	HRESULT hr = E_FAIL;
+
+	if(hInst || (hInst = CoLoadLibrary(CComBSTR(fullpath), TRUE)))
+	{
+		typedef HRESULT (__stdcall * PDllGetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
+		PDllGetClassObject p = (PDllGetClassObject)GetProcAddress(hInst, "DllGetClassObject");
+
+		if(p && FAILED(hr = p(clsid, iid, ppv)))
+		{
+			CComPtr<IClassFactory> pCF;
+			if(SUCCEEDED(hr = p(clsid, __uuidof(IClassFactory), (void**)&pCF)))
+			{
+				hr = pCF->CreateInstance(NULL, iid, ppv);
+			}
+		}
+	}
+
+	if(FAILED(hr) && hInst && !fFound)
+	{
+		CoFreeLibrary(hInst);
+		return hr;
+	}
+
+	if(hInst && !fFound)
+	{
+		ExternalObject eo;
+		eo.path = fullpath;
+		eo.hInst = hInst;
+		eo.clsid = clsid;
+		s_extobjs.AddTail(eo);
+	}
+
+	return hr;
+}
+
+HRESULT LoadExternalFilter(LPCTSTR path, REFCLSID clsid, IBaseFilter** ppBF)
+{
+	return LoadExternalObject(path, clsid, __uuidof(IBaseFilter), (void**)ppBF);
+}
+
+HRESULT LoadExternalPropertyPage(IPersist* pP, REFCLSID clsid, IPropertyPage** ppPP)
+{
+	CLSID clsid2 = GUID_NULL;
+	if(FAILED(pP->GetClassID(&clsid2))) return E_FAIL;
+
+	POSITION pos = s_extobjs.GetHeadPosition();
+	while(pos)
+	{
+		ExternalObject& eo = s_extobjs.GetNext(pos);
+		if(eo.clsid == clsid2)
+		{
+			return LoadExternalObject(eo.path, clsid, __uuidof(IPropertyPage), (void**)ppPP);
+		}
+	}
+
+	return E_FAIL;
+}
+
+void UnloadExternalObjects()
+{
+	POSITION pos = s_extobjs.GetHeadPosition();
+	while(pos)
+	{
+		ExternalObject& eo = s_extobjs.GetNext(pos);
+		CoFreeLibrary(eo.hInst);
+	}
+	s_extobjs.RemoveAll();
+}
+
+CString MakeFullPath(LPCTSTR path)
+{
+	CString full(path);
+	full.Replace('/', '\\');
+
+	CString fn;
+	fn.ReleaseBuffer(GetModuleFileName(AfxGetInstanceHandle(), fn.GetBuffer(MAX_PATH), MAX_PATH));
+	CPath p(fn);
+
+	if(full.GetLength() >= 2 && full[0] == '\\' && full[1] != '\\')
+	{
+		p.StripToRoot();
+		full = CString(p) + full.Mid(1); 
+	}
+	else if(full.Find(_T(":\\")) < 0)
+	{
+		p.RemoveFileSpec();
+		p.AddBackslash();
+		full = CString(p) + full;
+	}
+
+	CPath c(full);
+	c.Canonicalize();
+	return CString(c);
+}
+
+//
+
+CString GetMediaTypeName(const GUID& guid)
+{
+	CString ret = guid == GUID_NULL 
+		? _T("Any type") 
+		: CString(GuidNames[guid]);
+
+	if(ret == _T("FOURCC GUID"))
+	{
+		CString str;
+		if(guid.Data1 >= 0x10000)
+			str.Format(_T("Video: %c%c%c%c"), (guid.Data1>>0)&0xff, (guid.Data1>>8)&0xff, (guid.Data1>>16)&0xff, (guid.Data1>>24)&0xff);
+		else
+			str.Format(_T("Audio: 0x%08x"), guid.Data1);
+		ret = str;
+	}
+	else if(ret == _T("Unknown GUID Name"))
+	{
+		WCHAR null[128] = {0}, buff[128];
+		StringFromGUID2(GUID_NULL, null, 127);
+		ret = CString(CStringW(StringFromGUID2(guid, buff, 127) ? buff : null));
+	}
+
+	return ret;
+}
+
+GUID GUIDFromCString(CString str)
+{
+	GUID guid = GUID_NULL;
+	HRESULT hr = CLSIDFromString(CComBSTR(str), &guid);
+	ASSERT(SUCCEEDED(hr));
+	return guid;
+}
+
+HRESULT GUIDFromCString(CString str, GUID& guid)
+{
+	guid = GUID_NULL;
+	return CLSIDFromString(CComBSTR(str), &guid);
+}
+
+CString CStringFromGUID(const GUID& guid)
+{
+	WCHAR null[128] = {0}, buff[128];
+	StringFromGUID2(GUID_NULL, null, 127);
+	return CString(StringFromGUID2(guid, buff, 127) > 0 ? buff : null);
+}
+
+CStringW UTF8To16(LPCSTR utf8)
+{
+	CStringW str;
+	int n = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)-1;
+	if(n < 0) return str;
+	str.ReleaseBuffer(MultiByteToWideChar(CP_UTF8, 0, utf8, -1, str.GetBuffer(n), n+1)-1);
+	return str;
+}
+
+CStringA UTF16To8(LPCWSTR utf16)
+{
+	CStringA str;
+	int n = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL, 0, NULL, NULL)-1;
+	if(n < 0) return str;
+	str.ReleaseBuffer(WideCharToMultiByte(CP_UTF8, 0, utf16, -1, str.GetBuffer(n), n+1, NULL, NULL)-1);
+	return str;
+}
+
+static struct {LPCSTR name, iso6392, iso6391;} s_isolangs[] = 
+{
+	{"Abkhazian", "abk", "ab"},
+	{"Achinese", "ace", ""},
+	{"Acoli", "ach", ""},
+	{"Adangme", "ada", ""},
+	{"Afar", "aar", "aa"},
+	{"Afrihili", "afh", ""},
+	{"Afrikaans", "afr", "af"},
+	{"Afro-Asiatic (Other)", "afa", ""},
+	{"Akan", "aka", "ak"},
+	{"Akkadian", "akk", ""},
+	{"Albanian", "alb", "sq"},
+	{"Albanian", "sqi", "sq"},
+	{"Aleut", "ale", ""},
+	{"Algonquian languages", "alg", ""},
+	{"Altaic (Other)", "tut", ""},
+	{"Amharic", "amh", "am"},
+	{"Apache languages", "apa", ""},
+	{"Arabic", "ara", "ar"},
+	{"Aragonese", "arg", "an"},
+	{"Aramaic", "arc", ""},
+	{"Arapaho", "arp", ""},
+	{"Araucanian", "arn", ""},
+	{"Arawak", "arw", ""},
+	{"Armenian", "arm", "hy"},
+	{"Armenian", "hye", "hy"},
+	{"Artificial (Other)", "art", ""},
+	{"Assamese", "asm", "as"},
+	{"Asturian; Bable", "ast", ""},
+	{"Athapascan languages", "ath", ""},
+	{"Australian languages", "aus", ""},
+	{"Austronesian (Other)", "map", ""},
+	{"Avaric", "ava", "av"},
+	{"Avestan", "ave", "ae"},
+	{"Awadhi", "awa", ""},
+	{"Aymara", "aym", "ay"},
+	{"Azerbaijani", "aze", "az"},
+	{"Bable; Asturian", "ast", ""},
+	{"Balinese", "ban", ""},
+	{"Baltic (Other)", "bat", ""},
+	{"Baluchi", "bal", ""},
+	{"Bambara", "bam", "bm"},
+	{"Bamileke languages", "bai", ""},
+	{"Banda", "bad", ""},
+	{"Bantu (Other)", "bnt", ""},
+	{"Basa", "bas", ""},
+	{"Bashkir", "bak", "ba"},
+	{"Basque", "baq", "eu"},
+	{"Basque", "eus", "eu"},
+	{"Batak (Indonesia)", "btk", ""},
+	{"Beja", "bej", ""},
+	{"Belarusian", "bel", "be"},
+	{"Bemba", "bem", ""},
+	{"Bengali", "ben", "bn"},
+	{"Berber (Other)", "ber", ""},
+	{"Bhojpuri", "bho", ""},
+	{"Bihari", "bih", "bh"},
+	{"Bikol", "bik", ""},
+	{"Bini", "bin", ""},
+	{"Bislama", "bis", "bi"},
+	{"Bokmål, Norwegian; Norwegian Bokmål", "nob", "nb"},
+	{"Bosnian", "bos", "bs"},
+	{"Braj", "bra", ""},
+	{"Breton", "bre", "br"},
+	{"Buginese", "bug", ""},
+	{"Bulgarian", "bul", "bg"},
+	{"Buriat", "bua", ""},
+	{"Burmese", "bur", "my"},
+	{"Burmese", "mya", "my"},
+	{"Caddo", "cad", ""},
+	{"Carib", "car", ""},
+	{"Spanish; Castilian", "spa", "es"},
+	{"Catalan", "cat", "ca"},
+	{"Caucasian (Other)", "cau", ""},
+	{"Cebuano", "ceb", ""},
+	{"Celtic (Other)", "cel", ""},
+	{"Central American Indian (Other)", "cai", ""},
+	{"Chagatai", "chg", ""},
+	{"Chamic languages", "cmc", ""},
+	{"Chamorro", "cha", "ch"},
+	{"Chechen", "che", "ce"},
+	{"Cherokee", "chr", ""},
+	{"Chewa; Chichewa; Nyanja", "nya", "ny"},
+	{"Cheyenne", "chy", ""},
+	{"Chibcha", "chb", ""},
+	{"Chichewa; Chewa; Nyanja", "nya", "ny"},
+	{"Chinese", "chi", "zh"},
+	{"Chinese", "zho", "zh"},
+	{"Chinook jargon", "chn", ""},
+	{"Chipewyan", "chp", ""},
+	{"Choctaw", "cho", ""},
+	{"Chuang; Zhuang", "zha", "za"},
+	{"Church Slavic; Old Church Slavonic", "chu", "cu"},
+	{"Old Church Slavonic; Old Slavonic; ", "chu", "cu"},
+	{"Church Slavonic; Old Bulgarian; Church Slavic;", "chu", "cu"},
+	{"Old Slavonic; Church Slavonic; Old Bulgarian;", "chu", "cu"},
+	{"Church Slavic; Old Church Slavonic", "chu", "cu"},
+	{"Chuukese", "chk", ""},
+	{"Chuvash", "chv", "cv"},
+	{"Coptic", "cop", ""},
+	{"Cornish", "cor", "kw"},
+	{"Corsican", "cos", "co"},
+	{"Cree", "cre", "cr"},
+	{"Creek", "mus", ""},
+	{"Creoles and pidgins (Other)", "crp", ""},
+	{"Creoles and pidgins,", "cpe", ""},
+	//   {"English-based (Other)", "", ""},
+	{"Creoles and pidgins,", "cpf", ""},
+	//   {"French-based (Other)", "", ""},
+	{"Creoles and pidgins,", "cpp", ""},
+	//   {"Portuguese-based (Other)", "", ""},
+	{"Croatian", "scr", "hr"},
+	{"Croatian", "hrv", "hr"},
+	{"Cushitic (Other)", "cus", ""},
+	{"Czech", "cze", "cs"},
+	{"Czech", "ces", "cs"},
+	{"Dakota", "dak", ""},
+	{"Danish", "dan", "da"},
+	{"Dargwa", "dar", ""},
+	{"Dayak", "day", ""},
+	{"Delaware", "del", ""},
+	{"Dinka", "din", ""},
+	{"Divehi", "div", "dv"},
+	{"Dogri", "doi", ""},
+	{"Dogrib", "dgr", ""},
+	{"Dravidian (Other)", "dra", ""},
+	{"Duala", "dua", ""},
+	{"Dutch; Flemish", "dut", "nl"},
+	{"Dutch; Flemish", "nld", "nl"},
+	{"Dutch, Middle (ca. 1050-1350)", "dum", ""},
+	{"Dyula", "dyu", ""},
+	{"Dzongkha", "dzo", "dz"},
+	{"Efik", "efi", ""},
+	{"Egyptian (Ancient)", "egy", ""},
+	{"Ekajuk", "eka", ""},
+	{"Elamite", "elx", ""},
+	{"English", "eng", "en"},
+	{"English, Middle (1100-1500)", "enm", ""},
+	{"English, Old (ca.450-1100)", "ang", ""},
+	{"Esperanto", "epo", "eo"},
+	{"Estonian", "est", "et"},
+	{"Ewe", "ewe", "ee"},
+	{"Ewondo", "ewo", ""},
+	{"Fang", "fan", ""},
+	{"Fanti", "fat", ""},
+	{"Faroese", "fao", "fo"},
+	{"Fijian", "fij", "fj"},
+	{"Finnish", "fin", "fi"},
+	{"Finno-Ugrian (Other)", "fiu", ""},
+	{"Flemish; Dutch", "dut", "nl"},
+	{"Flemish; Dutch", "nld", "nl"},
+	{"Fon", "fon", ""},
+	{"French", "fre", "fr"},
+	{"French", "fra*", "fr"},
+	{"French, Middle (ca.1400-1600)", "frm", ""},
+	{"French, Old (842-ca.1400)", "fro", ""},
+	{"Frisian", "fry", "fy"},
+	{"Friulian", "fur", ""},
+	{"Fulah", "ful", "ff"},
+	{"Ga", "gaa", ""},
+	{"Gaelic; Scottish Gaelic", "gla", "gd"},
+	{"Gallegan", "glg", "gl"},
+	{"Ganda", "lug", "lg"},
+	{"Gayo", "gay", ""},
+	{"Gbaya", "gba", ""},
+	{"Geez", "gez", ""},
+	{"Georgian", "geo", "ka"},
+	{"Georgian", "kat", "ka"},
+	{"German", "ger", "de"},
+	{"German", "deu", "de"},
+	{"German, Low; Saxon, Low; Low German; Low Saxon", "nds", ""},
+	{"German, Middle High (ca.1050-1500)", "gmh", ""},
+	{"German, Old High (ca.750-1050)", "goh", ""},
+	{"Germanic (Other)", "gem", ""},
+	{"Gikuyu; Kikuyu", "kik", "ki"},
+	{"Gilbertese", "gil", ""},
+	{"Gondi", "gon", ""},
+	{"Gorontalo", "gor", ""},
+	{"Gothic", "got", ""},
+	{"Grebo", "grb", ""},
+	{"Greek, Ancient (to 1453)", "grc", ""},
+	{"Greek, Modern (1453-)", "gre", "el"},
+	{"Greek, Modern (1453-)", "ell", "el"},
+	{"Greenlandic; Kalaallisut", "kal", "kl"},
+	{"Guarani", "grn", "gn"},
+	{"Gujarati", "guj", "gu"},
+	{"Gwich´in", "gwi", ""},
+	{"Haida", "hai", ""},
+	{"Hausa", "hau", "ha"},
+	{"Hawaiian", "haw", ""},
+	{"Hebrew", "heb", "he"},
+	{"Herero", "her", "hz"},
+	{"Hiligaynon", "hil", ""},
+	{"Himachali", "him", ""},
+	{"Hindi", "hin", "hi"},
+	{"Hiri Motu", "hmo", "ho"},
+	{"Hittite", "hit", ""},
+	{"Hmong", "hmn", ""},
+	{"Hungarian", "hun", "hu"},
+	{"Hupa", "hup", ""},
+	{"Iban", "iba", ""},
+	{"Icelandic", "ice", "is"},
+	{"Icelandic", "isl", "is"},
+	{"Ido", "ido", "io"},
+	{"Igbo", "ibo", "ig"},
+	{"Ijo", "ijo", ""},
+	{"Iloko", "ilo", ""},
+	{"Inari Sami", "smn", ""},
+	{"Indic (Other)", "inc", ""},
+	{"Indo-European (Other)", "ine", ""},
+	{"Indonesian", "ind", "id"},
+	{"Ingush", "inh", ""},
+	{"Interlingua (International", "ina", "ia"},
+	//   {"Auxiliary Language Association)", "", ""},
+	{"Interlingue", "ile", "ie"},
+	{"Inuktitut", "iku", "iu"},
+	{"Inupiaq", "ipk", "ik"},
+	{"Iranian (Other)", "ira", ""},
+	{"Irish", "gle", "ga"},
+	{"Irish, Middle (900-1200)", "mga", ""},
+	{"Irish, Old (to 900)", "sga", ""},
+	{"Iroquoian languages", "iro", ""},
+	{"Italian", "ita", "it"},
+	{"Japanese", "jpn", "ja"},
+	{"Javanese", "jav", "jv"},
+	{"Judeo-Arabic", "jrb", ""},
+	{"Judeo-Persian", "jpr", ""},
+	{"Kabardian", "kbd", ""},
+	{"Kabyle", "kab", ""},
+	{"Kachin", "kac", ""},
+	{"Kalaallisut; Greenlandic", "kal", "kl"},
+	{"Kamba", "kam", ""},
+	{"Kannada", "kan", "kn"},
+	{"Kanuri", "kau", "kr"},
+	{"Kara-Kalpak", "kaa", ""},
+	{"Karen", "kar", ""},
+	{"Kashmiri", "kas", "ks"},
+	{"Kawi", "kaw", ""},
+	{"Kazakh", "kaz", "kk"},
+	{"Khasi", "kha", ""},
+	{"Khmer", "khm", "km"},
+	{"Khoisan (Other)", "khi", ""},
+	{"Khotanese", "kho", ""},
+	{"Kikuyu; Gikuyu", "kik", "ki"},
+	{"Kimbundu", "kmb", ""},
+	{"Kinyarwanda", "kin", "rw"},
+	{"Kirghiz", "kir", "ky"},
+	{"Komi", "kom", "kv"},
+	{"Kongo", "kon", "kg"},
+	{"Konkani", "kok", ""},
+	{"Korean", "kor", "ko"},
+	{"Kosraean", "kos", ""},
+	{"Kpelle", "kpe", ""},
+	{"Kru", "kro", ""},
+	{"Kuanyama; Kwanyama", "kua", "kj"},
+	{"Kumyk", "kum", ""},
+	{"Kurdish", "kur", "ku"},
+	{"Kurukh", "kru", ""},
+	{"Kutenai", "kut", ""},
+	{"Kwanyama, Kuanyama", "kua", "kj"},
+	{"Ladino", "lad", ""},
+	{"Lahnda", "lah", ""},
+	{"Lamba", "lam", ""},
+	{"Lao", "lao", "lo"},
+	{"Latin", "lat", "la"},
+	{"Latvian", "lav", "lv"},
+	{"Letzeburgesch; Luxembourgish", "ltz", "lb"},
+	{"Lezghian", "lez", ""},
+	{"Limburgan; Limburger; Limburgish", "lim", "li"},
+	{"Limburger; Limburgan; Limburgish;", "lim", "li"},
+	{"Limburgish; Limburger; Limburgan", "lim", "li"},
+	{"Lingala", "lin", "ln"},
+	{"Lithuanian", "lit", "lt"},
+	{"Low German; Low Saxon; German, Low; Saxon, Low", "nds", ""},
+	{"Low Saxon; Low German; Saxon, Low; German, Low", "nds", ""},
+	{"Lozi", "loz", ""},
+	{"Luba-Katanga", "lub", "lu"},
+	{"Luba-Lulua", "lua", ""},
+	{"Luiseno", "lui", ""},
+	{"Lule Sami", "smj", ""},
+	{"Lunda", "lun", ""},
+	{"Luo (Kenya and Tanzania)", "luo", ""},
+	{"Lushai", "lus", ""},
+	{"Luxembourgish; Letzeburgesch", "ltz", "lb"},
+	{"Macedonian", "mac", "mk"},
+	{"Macedonian", "mkd", "mk"},
+	{"Madurese", "mad", ""},
+	{"Magahi", "mag", ""},
+	{"Maithili", "mai", ""},
+	{"Makasar", "mak", ""},
+	{"Malagasy", "mlg", "mg"},
+	{"Malay", "may", "ms"},
+	{"Malay", "msa", "ms"},
+	{"Malayalam", "mal", "ml"},
+	{"Maltese", "mlt", "mt"},
+	{"Manchu", "mnc", ""},
+	{"Mandar", "mdr", ""},
+	{"Mandingo", "man", ""},
+	{"Manipuri", "mni", ""},
+	{"Manobo languages", "mno", ""},
+	{"Manx", "glv", "gv"},
+	{"Maori", "mao", "mi"},
+	{"Maori", "mri", "mi"},
+	{"Marathi", "mar", "mr"},
+	{"Mari", "chm", ""},
+	{"Marshallese", "mah", "mh"},
+	{"Marwari", "mwr", ""},
+	{"Masai", "mas", ""},
+	{"Mayan languages", "myn", ""},
+	{"Mende", "men", ""},
+	{"Micmac", "mic", ""},
+	{"Minangkabau", "min", ""},
+	{"Miscellaneous languages", "mis", ""},
+	{"Mohawk", "moh", ""},
+	{"Moldavian", "mol", "mo"},
+	{"Mon-Khmer (Other)", "mkh", ""},
+	{"Mongo", "lol", ""},
+	{"Mongolian", "mon", "mn"},
+	{"Mossi", "mos", ""},
+	{"Multiple languages", "mul", ""},
+	{"Munda languages", "mun", ""},
+	{"Nahuatl", "nah", ""},
+	{"Nauru", "nau", "na"},
+	{"Navaho, Navajo", "nav", "nv"},
+	{"Navajo; Navaho", "nav", "nv"},
+	{"Ndebele, North", "nde", "nd"},
+	{"Ndebele, South", "nbl", "nr"},
+	{"Ndonga", "ndo", "ng"},
+	{"Neapolitan", "nap", ""},
+	{"Nepali", "nep", "ne"},
+	{"Newari", "new", ""},
+	{"Nias", "nia", ""},
+	{"Niger-Kordofanian (Other)", "nic", ""},
+	{"Nilo-Saharan (Other)", "ssa", ""},
+	{"Niuean", "niu", ""},
+	{"Norse, Old", "non", ""},
+	{"North American Indian (Other)", "nai", ""},
+	{"Northern Sami", "sme", "se"},
+	{"North Ndebele", "nde", "nd"},
+	{"Norwegian", "nor", "no"},
+	{"Norwegian Bokmål; Bokmål, Norwegian", "nob", "nb"},
+	{"Norwegian Nynorsk; Nynorsk, Norwegian", "nno", "nn"},
+	{"Nubian languages", "nub", ""},
+	{"Nyamwezi", "nym", ""},
+	{"Nyanja; Chichewa; Chewa", "nya", "ny"},
+	{"Nyankole", "nyn", ""},
+	{"Nynorsk, Norwegian; Norwegian Nynorsk", "nno", "nn"},
+	{"Nyoro", "nyo", ""},
+	{"Nzima", "nzi", ""},
+	{"Occitan (post 1500},; Provençal", "oci", "oc"},
+	{"Ojibwa", "oji", "oj"},
+	{"Old Bulgarian; Old Slavonic; Church Slavonic;", "chu", "cu"},
+	{"Oriya", "ori", "or"},
+	{"Oromo", "orm", "om"},
+	{"Osage", "osa", ""},
+	{"Ossetian; Ossetic", "oss", "os"},
+	{"Ossetic; Ossetian", "oss", "os"},
+	{"Otomian languages", "oto", ""},
+	{"Pahlavi", "pal", ""},
+	{"Palauan", "pau", ""},
+	{"Pali", "pli", "pi"},
+	{"Pampanga", "pam", ""},
+	{"Pangasinan", "pag", ""},
+	{"Panjabi", "pan", "pa"},
+	{"Papiamento", "pap", ""},
+	{"Papuan (Other)", "paa", ""},
+	{"Persian", "per", "fa"},
+	{"Persian", "fas", "fa"},
+	{"Persian, Old (ca.600-400 B.C.)", "peo", ""},
+	{"Philippine (Other)", "phi", ""},
+	{"Phoenician", "phn", ""},
+	{"Pohnpeian", "pon", ""},
+	{"Polish", "pol", "pl"},
+	{"Portuguese", "por", "pt"},
+	{"Prakrit languages", "pra", ""},
+	{"Provençal; Occitan (post 1500)", "oci", "oc"},
+	{"Provençal, Old (to 1500)", "pro", ""},
+	{"Pushto", "pus", "ps"},
+	{"Quechua", "que", "qu"},
+	{"Raeto-Romance", "roh", "rm"},
+	{"Rajasthani", "raj", ""},
+	{"Rapanui", "rap", ""},
+	{"Rarotongan", "rar", ""},
+	{"Reserved for local use", "qaa-qtz", ""},
+	{"Romance (Other)", "roa", ""},
+	{"Romanian", "rum", "ro"},
+	{"Romanian", "ron", "ro"},
+	{"Romany", "rom", ""},
+	{"Rundi", "run", "rn"},
+	{"Russian", "rus", "ru"},
+	{"Salishan languages", "sal", ""},
+	{"Samaritan Aramaic", "sam", ""},
+	{"Sami languages (Other)", "smi", ""},
+	{"Samoan", "smo", "sm"},
+	{"Sandawe", "sad", ""},
+	{"Sango", "sag", "sg"},
+	{"Sanskrit", "san", "sa"},
+	{"Santali", "sat", ""},
+	{"Sardinian", "srd", "sc"},
+	{"Sasak", "sas", ""},
+	{"Saxon, Low; German, Low; Low Saxon; Low German", "nds", ""},
+	{"Scots", "sco", ""},
+	{"Scottish Gaelic; Gaelic", "gla", "gd"},
+	{"Selkup", "sel", ""},
+	{"Semitic (Other)", "sem", ""},
+	{"Serbian", "scc", "sr"},
+	{"Serbian", "srp", "sr"},
+	{"Serer", "srr", ""},
+	{"Shan", "shn", ""},
+	{"Shona", "sna", "sn"},
+	{"Sichuan Yi", "iii", "ii"},
+	{"Sidamo", "sid", ""},
+	{"Sign languages", "sgn", ""},
+	{"Siksika", "bla", ""},
+	{"Sindhi", "snd", "sd"},
+	{"Sinhalese", "sin", "si"},
+	{"Sino-Tibetan (Other)", "sit", ""},
+	{"Siouan languages", "sio", ""},
+	{"Skolt Sami", "sms", ""},
+	{"Slave (Athapascan)", "den", ""},
+	{"Slavic (Other)", "sla", ""},
+	{"Slovak", "slo", "sk"},
+	{"Slovak", "slk", "sk"},
+	{"Slovenian", "slv", "sl"},
+	{"Sogdian", "sog", ""},
+	{"Somali", "som", "so"},
+	{"Songhai", "son", ""},
+	{"Soninke", "snk", ""},
+	{"Sorbian languages", "wen", ""},
+	{"Sotho, Northern", "nso", ""},
+	{"Sotho, Southern", "sot", "st"},
+	{"South American Indian (Other)", "sai", ""},
+	{"Southern Sami", "sma", ""},
+	{"South Ndebele", "nbl", "nr"},
+	{"Spanish; Castilian", "spa", "es"},
+	{"Sukuma", "suk", ""},
+	{"Sumerian", "sux", ""},
+	{"Sundanese", "sun", "su"},
+	{"Susu", "sus", ""},
+	{"Swahili", "swa", "sw"},
+	{"Swati", "ssw", "ss"},
+	{"Swedish", "swe", "sv"},
+	{"Syriac", "syr", ""},
+	{"Tagalog", "tgl", "tl"},
+	{"Tahitian", "tah", "ty"},
+	{"Tai (Other)", "tai", ""},
+	{"Tajik", "tgk", "tg"},
+	{"Tamashek", "tmh", ""},
+	{"Tamil", "tam", "ta"},
+	{"Tatar", "tat", "tt"},
+	{"Telugu", "tel", "te"},
+	{"Tereno", "ter", ""},
+	{"Tetum", "tet", ""},
+	{"Thai", "tha", "th"},
+	{"Tibetan", "tib", "bo"},
+	{"Tibetan", "bod", "bo"},
+	{"Tigre", "tig", ""},
+	{"Tigrinya", "tir", "ti"},
+	{"Timne", "tem", ""},
+	{"Tiv", "tiv", ""},
+	{"Tlingit", "tli", ""},
+	{"Tok Pisin", "tpi", ""},
+	{"Tokelau", "tkl", ""},
+	{"Tonga (Nyasa)", "tog", ""},
+	{"Tonga (Tonga Islands)", "ton", "to"},
+	{"Tsimshian", "tsi", ""},
+	{"Tsonga", "tso", "ts"},
+	{"Tswana", "tsn", "tn"},
+	{"Tumbuka", "tum", ""},
+	{"Tupi languages", "tup", ""},
+	{"Turkish", "tur", "tr"},
+	{"Turkish, Ottoman (1500-1928)", "ota", ""},
+	{"Turkmen", "tuk", "tk"},
+	{"Tuvalu", "tvl", ""},
+	{"Tuvinian", "tyv", ""},
+	{"Twi", "twi", "tw"},
+	{"Ugaritic", "uga", ""},
+	{"Uighur", "uig", "ug"},
+	{"Ukrainian", "ukr", "uk"},
+	{"Umbundu", "umb", ""},
+	{"Undetermined", "und", ""},
+	{"Urdu", "urd", "ur"},
+	{"Uzbek", "uzb", "uz"},
+	{"Vai", "vai", ""},
+	{"Venda", "ven", "ve"},
+	{"Vietnamese", "vie", "vi"},
+	{"Volapük", "vol", "vo"},
+	{"Votic", "vot", ""},
+	{"Wakashan languages", "wak", ""},
+	{"Walamo", "wal", ""},
+	{"Walloon", "wln", "wa"},
+	{"Waray", "war", ""},
+	{"Washo", "was", ""},
+	{"Welsh", "wel", "cy"},
+	{"Welsh", "cym", "cy"},
+	{"Wolof", "wol", "wo"},
+	{"Xhosa", "xho", "xh"},
+	{"Yakut", "sah", ""},
+	{"Yao", "yao", ""},
+	{"Yapese", "yap", ""},
+	{"Yiddish", "yid", "yi"},
+	{"Yoruba", "yor", "yo"},
+	{"Yupik languages", "ypk", ""},
+	{"Zande", "znd", ""},
+	{"Zapotec", "zap", ""},
+	{"Zenaga", "zen", ""},
+	{"Zhuang; Chuang", "zha", "za"},
+	{"Zulu", "zul", "zu"},
+	{"Zuni", "zun", ""},
+	{"Classical Newari", "nwc", ""},
+	{"Klingon", "tlh", ""},
+	{"Blin", "byn", ""},
+	{"Lojban", "jbo", ""},
+	{"Lower Sorbian", "dsb", ""},
+	{"Upper Sorbian", "hsb", ""},
+	{"Kashubian", "csb", ""},
+	{"Crimean Turkish", "crh", ""},
+	{"Erzya", "myv", ""},
+	{"Moksha", "mdf", ""},
+	{"Karachay-Balkar", "krc", ""},
+	{"Adyghe", "ady", ""},
+	{"Udmurt", "udm", ""},
+	{"Dargwa", "dar", ""},
+	{"Ingush", "inh", ""},
+	{"Nogai", "nog", ""},
+	{"Haitian", "hat", "ht"},
+	{"Kalmyk", "xal", ""},
+	{"", "", ""},
+};
+
+CString ISO6391ToLanguage(LPCSTR code)
+{
+	CHAR tmp[2+1];
+	strncpy(tmp, code, 2);
+	tmp[2] = 0;
+	_strlwr(tmp);
+	for(int i = 0, j = countof(s_isolangs); i < j; i++)
+		if(!strcmp(s_isolangs[i].iso6391, tmp))
+		{
+			CString ret = CString(CStringA(s_isolangs[i].name));
+			int i = ret.Find(';');
+			if(i > 0) ret = ret.Left(i);
+			return ret;
+		}
+	return(_T(""));
+}
+
+CString ISO6392ToLanguage(LPCSTR code)
+{
+	CHAR tmp[3+1];
+	strncpy(tmp, code, 3);
+	tmp[3] = 0;
+	_strlwr(tmp);
+	for(int i = 0, j = countof(s_isolangs); i < j; i++)
+	{
+		if(!strcmp(s_isolangs[i].iso6392, tmp))
+		{
+			CString ret = CString(CStringA(s_isolangs[i].name));
+			int i = ret.Find(';');
+			if(i > 0) ret = ret.Left(i);
+			return ret;
+		}
+	}
+	return _T("");
+}
+
+CString ISO6391To6392(LPCSTR code)
+{
+	CHAR tmp[2+1];
+	strncpy(tmp, code, 2);
+	tmp[2] = 0;
+	_strlwr(tmp);
+	for(int i = 0, j = countof(s_isolangs); i < j; i++)
+		if(!strcmp(s_isolangs[i].iso6391, tmp))
+			return CString(CStringA(s_isolangs[i].iso6392));
+	return _T("");
+}
+
+CString ISO6392To6391(LPCSTR code)
+{
+	CHAR tmp[3+1];
+	strncpy(tmp, code, 3);
+	tmp[3] = 0;
+	_strlwr(tmp);
+	for(int i = 0, j = countof(s_isolangs); i < j; i++)
+		if(!strcmp(s_isolangs[i].iso6392, tmp))
+			return CString(CStringA(s_isolangs[i].iso6391));
+	return _T("");
+}
+
+CString LanguageToISO6392(LPCTSTR lang)
+{
+	CString str = lang;
+	str.MakeLower();
+	for(int i = 0, j = countof(s_isolangs); i < j; i++)
+	{
+		CAtlList<CString> sl;
+		Explode(CString(s_isolangs[i].name), sl, ';');
+		POSITION pos = sl.GetHeadPosition();
+		while(pos)
+		{
+			if(!str.CompareNoCase(sl.GetNext(pos)))
+				return CString(s_isolangs[i].iso6392);
+		}
+	}
+	return _T("");
+}
+
+int MakeAACInitData(BYTE* pData, int profile, int freq, int channels)
+{
+	int srate_idx;
+
+	if(92017 <= freq) srate_idx = 0;
+	else if(75132 <= freq) srate_idx = 1;
+	else if(55426 <= freq) srate_idx = 2;
+	else if(46009 <= freq) srate_idx = 3;
+	else if(37566 <= freq) srate_idx = 4;
+	else if(27713 <= freq) srate_idx = 5;
+	else if(23004 <= freq) srate_idx = 6;
+	else if(18783 <= freq) srate_idx = 7;
+	else if(13856 <= freq) srate_idx = 8;
+	else if(11502 <= freq) srate_idx = 9;
+	else if(9391 <= freq) srate_idx = 10;
+	else srate_idx = 11;
+
+	pData[0] = ((abs(profile) + 1) << 3) | ((srate_idx & 0xe) >> 1);
+	pData[1] = ((srate_idx & 0x1) << 7) | (channels << 3);
+
+	int ret = 2;
+
+	if(profile < 0)
+	{
+		freq *= 2;
+
+		if(92017 <= freq) srate_idx = 0;
+		else if(75132 <= freq) srate_idx = 1;
+		else if(55426 <= freq) srate_idx = 2;
+		else if(46009 <= freq) srate_idx = 3;
+		else if(37566 <= freq) srate_idx = 4;
+		else if(27713 <= freq) srate_idx = 5;
+		else if(23004 <= freq) srate_idx = 6;
+		else if(18783 <= freq) srate_idx = 7;
+		else if(13856 <= freq) srate_idx = 8;
+		else if(11502 <= freq) srate_idx = 9;
+		else if(9391 <= freq) srate_idx = 10;
+		else srate_idx = 11;
+
+		pData[2] = 0x2B7>>3;
+		pData[3] = (BYTE)((0x2B7<<5) | 5);
+		pData[4] = (1<<7) | (srate_idx<<3);
+
+		ret = 5;
+	}
+
+	return(ret);
+}
+
+BOOL CFileGetStatus(LPCTSTR lpszFileName, CFileStatus& status)
+{
+	try
+	{
+		return CFile::GetStatus(lpszFileName, status);
+	}
+	catch(CException* e)
+	{
+		// MFCBUG: E_INVALIDARG / "Parameter is incorrect" is thrown for certain cds (vs2003)
+		// http://groups.google.co.uk/groups?hl=en&lr=&ie=UTF-8&threadm=OZuXYRzWDHA.536%40TK2MSFTNGP10.phx.gbl&rnum=1&prev=/groups%3Fhl%3Den%26lr%3D%26ie%3DISO-8859-1
+		TRACE(_T("CFile::GetStatus has thrown an exception\n"));
+		e->Delete();
+		return false;
+	}
+}
+
+// filter registration helpers
+
+bool DeleteRegKey(LPCTSTR pszKey, LPCTSTR pszSubkey)
+{
+	bool bOK = false;
+
+	HKEY hKey;
+	LONG ec = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, pszKey, 0, KEY_ALL_ACCESS, &hKey);
+	if(ec == ERROR_SUCCESS)
+	{
+		if(pszSubkey != 0)
+			ec = ::RegDeleteKey(hKey, pszSubkey);
+
+		bOK = (ec == ERROR_SUCCESS);
+
+		::RegCloseKey(hKey);
+	}
+
+	return bOK;
+}
+
+bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValueName, LPCTSTR pszValue)
+{
+	bool bOK = false;
+
+	CString szKey(pszKey);
+	if(pszSubkey != 0)
+		szKey += CString(_T("\\")) + pszSubkey;
+
+	HKEY hKey;
+	LONG ec = ::RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0);
+	if(ec == ERROR_SUCCESS)
+	{
+		if(pszValue != 0)
+		{
+			ec = ::RegSetValueEx(hKey, pszValueName, 0, REG_SZ,
+				reinterpret_cast<BYTE*>(const_cast<LPTSTR>(pszValue)),
+				(_tcslen(pszValue) + 1) * sizeof(TCHAR));
+		}
+
+		bOK = (ec == ERROR_SUCCESS);
+
+		::RegCloseKey(hKey);
+	}
+
+	return bOK;
+}
+
+bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValue)
+{
+	return SetRegKeyValue(pszKey, pszSubkey, 0, pszValue);
+}
+
+void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, LPCTSTR chkbytes, LPCTSTR ext, ...)
+{
+	CString null = CStringFromGUID(GUID_NULL);
+	CString majortype = CStringFromGUID(MEDIATYPE_Stream);
+	CString subtype = CStringFromGUID(subtype2);
+
+	SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("0"), chkbytes);
+	SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
+	
+	DeleteRegKey(_T("Media Type\\") + null, subtype);
+
+	va_list marker;
+	va_start(marker, ext);
+	for(; ext; ext = va_arg(marker, LPCTSTR))
+		DeleteRegKey(_T("Media Type\\Extensions"), ext);
+	va_end(marker);
+}
+
+void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, const CAtlList<CString>& chkbytes, LPCTSTR ext, ...)
+{
+	CString null = CStringFromGUID(GUID_NULL);
+	CString majortype = CStringFromGUID(MEDIATYPE_Stream);
+	CString subtype = CStringFromGUID(subtype2);
+
+	POSITION pos = chkbytes.GetHeadPosition();
+	for(int i = 0; pos; i++)
+	{
+		CString idx;
+		idx.Format(_T("%d"), i);
+		SetRegKeyValue(_T("Media Type\\") + majortype, subtype, idx, chkbytes.GetNext(pos));
+	}
+
+	SetRegKeyValue(_T("Media Type\\") + majortype, subtype, _T("Source Filter"), CStringFromGUID(clsid));
+	
+	DeleteRegKey(_T("Media Type\\") + null, subtype);
+
+	va_list marker;
+	va_start(marker, ext);
+	for(; ext; ext = va_arg(marker, LPCTSTR))
+		DeleteRegKey(_T("Media Type\\Extensions"), ext);
+	va_end(marker);
+}
+
+void UnRegisterSourceFilter(const GUID& subtype)
+{
+	DeleteRegKey(_T("Media Type\\") + CStringFromGUID(MEDIATYPE_Stream), CStringFromGUID(subtype));
+}
diff --git a/vsfilter/dsutil/DSUtil.h b/vsfilter/dsutil/DSUtil.h
new file mode 100644
index 0000000000000000000000000000000000000000..640ae28865f27850cc080bbf6ccd3579f95d8cc3
--- /dev/null
+++ b/vsfilter/dsutil/DSUtil.h
@@ -0,0 +1,203 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+/*
+ * Removing this section, don't want automatic linking.
+ * Use project dependencies in VS to link the right things.
+ * -jfs
+ *
+#ifdef UNICODE
+#ifdef DEBUG
+#pragma comment(lib, "dsutilDU")
+#else
+#pragma comment(lib, "dsutilRU")
+#endif
+#else
+#ifdef DEBUG
+#pragma comment(lib, "dsutilD")
+#else
+#pragma comment(lib, "dsutilR")
+#endif
+#endif
+*/
+
+#include "NullRenderers.h"
+//#include "MediaTypes.h"
+#include "MediaTypeEx.h"
+#include "vd.h"
+#include "text.h"
+
+extern void DumpStreamConfig(TCHAR* fn, IAMStreamConfig* pAMVSCCap);
+extern int CountPins(IBaseFilter* pBF, int& nIn, int& nOut, int& nInC, int& nOutC);
+extern bool IsSplitter(IBaseFilter* pBF, bool fCountConnectedOnly = false);
+extern bool IsMultiplexer(IBaseFilter* pBF, bool fCountConnectedOnly = false);
+extern bool IsStreamStart(IBaseFilter* pBF);
+extern bool IsStreamEnd(IBaseFilter* pBF);
+extern bool IsVideoRenderer(IBaseFilter* pBF);
+extern bool IsAudioWaveRenderer(IBaseFilter* pBF);
+extern IBaseFilter* GetUpStreamFilter(IBaseFilter* pBF, IPin* pInputPin = NULL);
+extern IPin* GetUpStreamPin(IBaseFilter* pBF, IPin* pInputPin = NULL);
+extern IPin* GetFirstPin(IBaseFilter* pBF, PIN_DIRECTION dir = PINDIR_INPUT);
+extern IPin* GetFirstDisconnectedPin(IBaseFilter* pBF, PIN_DIRECTION dir);
+extern void NukeDownstream(IBaseFilter* pBF, IFilterGraph* pFG);
+extern void NukeDownstream(IPin* pPin, IFilterGraph* pFG);
+extern IBaseFilter* FindFilter(LPCWSTR clsid, IFilterGraph* pFG);
+extern IBaseFilter* FindFilter(const CLSID& clsid, IFilterGraph* pFG);
+extern CStringW GetFilterName(IBaseFilter* pBF);
+extern CStringW GetPinName(IPin* pPin);
+extern IFilterGraph* GetGraphFromFilter(IBaseFilter* pBF);
+extern IBaseFilter* GetFilterFromPin(IPin* pPin);
+extern IPin* AppendFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB);
+extern IPin* InsertFilter(IPin* pPin, CString DisplayName, IGraphBuilder* pGB);
+extern void ExtractMediaTypes(IPin* pPin, CAtlArray<GUID>& types);
+extern void ExtractMediaTypes(IPin* pPin, CAtlList<CMediaType>& mts);
+extern void ShowPPage(CString DisplayName, HWND hParentWnd);
+extern void ShowPPage(IUnknown* pUnknown, HWND hParentWnd);
+extern CLSID GetCLSID(IBaseFilter* pBF);
+extern CLSID GetCLSID(IPin* pPin);
+extern bool IsCLSIDRegistered(LPCTSTR clsid);
+extern bool IsCLSIDRegistered(const CLSID& clsid);
+extern void CStringToBin(CString str, CAtlArray<BYTE>& data);
+extern CString BinToCString(BYTE* ptr, int len);
+typedef enum {CDROM_NotFound, CDROM_Audio, CDROM_VideoCD, CDROM_DVDVideo, CDROM_Unknown} cdrom_t;
+extern cdrom_t GetCDROMType(TCHAR drive, CAtlList<CString>& files);
+extern CString GetDriveLabel(TCHAR drive);
+extern bool GetKeyFrames(CString fn, CUIntArray& kfs);
+extern DVD_HMSF_TIMECODE RT2HMSF(REFERENCE_TIME rt, double fps = 0);
+extern REFERENCE_TIME HMSF2RT(DVD_HMSF_TIMECODE hmsf, double fps = 0);
+extern void memsetd(void* dst, unsigned int c, int nbytes);
+extern bool ExtractBIH(const AM_MEDIA_TYPE* pmt, BITMAPINFOHEADER* bih);
+extern bool ExtractBIH(IMediaSample* pMS, BITMAPINFOHEADER* bih);
+extern bool ExtractDim(const AM_MEDIA_TYPE* pmt, int& w, int& h, int& arx, int& ary);
+extern bool MakeMPEG2MediaType(CMediaType& mt, BYTE* seqhdr, DWORD len, int w, int h);
+extern unsigned __int64 GetFileVersion(LPCTSTR fn);
+extern bool CreateFilter(CStringW DisplayName, IBaseFilter** ppBF, CStringW& FriendlyName);
+extern IBaseFilter* AppendFilter(IPin* pPin, IMoniker* pMoniker, IGraphBuilder* pGB);
+extern CStringW GetFriendlyName(CStringW DisplayName);
+extern HRESULT LoadExternalObject(LPCTSTR path, REFCLSID clsid, REFIID iid, void** ppv);
+extern HRESULT LoadExternalFilter(LPCTSTR path, REFCLSID clsid, IBaseFilter** ppBF);
+extern HRESULT LoadExternalPropertyPage(IPersist* pP, REFCLSID clsid, IPropertyPage** ppPP);
+extern void UnloadExternalObjects();
+extern CString MakeFullPath(LPCTSTR path);
+extern CString GetMediaTypeName(const GUID& guid);
+extern GUID GUIDFromCString(CString str);
+extern HRESULT GUIDFromCString(CString str, GUID& guid);
+extern CString CStringFromGUID(const GUID& guid);
+extern CStringW UTF8To16(LPCSTR utf8);
+extern CStringA UTF16To8(LPCWSTR utf16);
+extern CString ISO6391ToLanguage(LPCSTR code);
+extern CString ISO6392ToLanguage(LPCSTR code);
+extern CString ISO6391To6392(LPCSTR code);
+extern CString ISO6392To6391(LPCSTR code);
+extern CString LanguageToISO6392(LPCTSTR lang);
+extern int MakeAACInitData(BYTE* pData, int profile, int freq, int channels);
+extern BOOL CFileGetStatus(LPCTSTR lpszFileName, CFileStatus& status);
+extern bool DeleteRegKey(LPCTSTR pszKey, LPCTSTR pszSubkey);
+extern bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValueName, LPCTSTR pszValue);
+extern bool SetRegKeyValue(LPCTSTR pszKey, LPCTSTR pszSubkey, LPCTSTR pszValue);
+extern void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, LPCTSTR chkbytes, LPCTSTR ext = NULL, ...);
+extern void RegisterSourceFilter(const CLSID& clsid, const GUID& subtype2, const CAtlList<CString>& chkbytes, LPCTSTR ext = NULL, ...);
+extern void UnRegisterSourceFilter(const GUID& subtype);
+
+class CPinInfo : public PIN_INFO
+{
+public:
+	CPinInfo() {pFilter = NULL;}
+	~CPinInfo() {if(pFilter) pFilter->Release();}
+};
+
+class CFilterInfo : public FILTER_INFO
+{
+public:
+	CFilterInfo() {pGraph = NULL;}
+	~CFilterInfo() {if(pGraph) pGraph->Release();}
+};
+
+#define BeginEnumFilters(pFilterGraph, pEnumFilters, pBaseFilter) \
+	{CComPtr<IEnumFilters> pEnumFilters; \
+	if(pFilterGraph && SUCCEEDED(pFilterGraph->EnumFilters(&pEnumFilters))) \
+	{ \
+		for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
+		{ \
+
+#define EndEnumFilters }}}
+
+#define BeginEnumCachedFilters(pGraphConfig, pEnumFilters, pBaseFilter) \
+	{CComPtr<IEnumFilters> pEnumFilters; \
+	if(pGraphConfig && SUCCEEDED(pGraphConfig->EnumCacheFilter(&pEnumFilters))) \
+	{ \
+		for(CComPtr<IBaseFilter> pBaseFilter; S_OK == pEnumFilters->Next(1, &pBaseFilter, 0); pBaseFilter = NULL) \
+		{ \
+
+#define EndEnumCachedFilters }}}
+
+#define BeginEnumPins(pBaseFilter, pEnumPins, pPin) \
+	{CComPtr<IEnumPins> pEnumPins; \
+	if(pBaseFilter && SUCCEEDED(pBaseFilter->EnumPins(&pEnumPins))) \
+	{ \
+		for(CComPtr<IPin> pPin; S_OK == pEnumPins->Next(1, &pPin, 0); pPin = NULL) \
+		{ \
+
+#define EndEnumPins }}}
+
+#define BeginEnumMediaTypes(pPin, pEnumMediaTypes, pMediaType) \
+	{CComPtr<IEnumMediaTypes> pEnumMediaTypes; \
+	if(pPin && SUCCEEDED(pPin->EnumMediaTypes(&pEnumMediaTypes))) \
+	{ \
+		AM_MEDIA_TYPE* pMediaType = NULL; \
+		for(; S_OK == pEnumMediaTypes->Next(1, &pMediaType, NULL); DeleteMediaType(pMediaType), pMediaType = NULL) \
+		{ \
+
+#define EndEnumMediaTypes(pMediaType) } if(pMediaType) DeleteMediaType(pMediaType); }}
+
+#define BeginEnumSysDev(clsid, pMoniker) \
+	{CComPtr<ICreateDevEnum> pDevEnum4$##clsid; \
+	pDevEnum4$##clsid.CoCreateInstance(CLSID_SystemDeviceEnum); \
+	CComPtr<IEnumMoniker> pClassEnum4$##clsid; \
+	if(SUCCEEDED(pDevEnum4$##clsid->CreateClassEnumerator(clsid, &pClassEnum4$##clsid, 0)) \
+	&& pClassEnum4$##clsid) \
+	{ \
+		for(CComPtr<IMoniker> pMoniker; pClassEnum4$##clsid->Next(1, &pMoniker, 0) == S_OK; pMoniker = NULL) \
+		{ \
+
+#define EndEnumSysDev }}}
+
+#define QI(i) (riid == __uuidof(i)) ? GetInterface((i*)this, ppv) :
+#define QI2(i) (riid == IID_##i) ? GetInterface((i*)this, ppv) :
+
+template <typename T> __inline void INITDDSTRUCT(T& dd)
+{
+    ZeroMemory(&dd, sizeof(dd));
+    dd.dwSize = sizeof(dd);
+}
+
+#define countof(array) (sizeof(array)/sizeof(array[0]))
+
+template <class T>
+static CUnknown* WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT* phr)
+{
+	*phr = S_OK;
+    CUnknown* punk = new T(lpunk, phr);
+    if(punk == NULL) *phr = E_OUTOFMEMORY;
+	return punk;
+}
diff --git a/vsfilter/dsutil/FontInstaller.cpp b/vsfilter/dsutil/FontInstaller.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c74700d1ab9053957af9ace2a12820eea03d7d51
--- /dev/null
+++ b/vsfilter/dsutil/FontInstaller.cpp
@@ -0,0 +1,94 @@
+#include "StdAfx.h"
+#include ".\fontinstaller.h"
+
+CFontInstaller::CFontInstaller()
+{
+	if(HMODULE hGdi = GetModuleHandle(_T("gdi32.dll")))
+	{
+		pAddFontMemResourceEx = (HANDLE (WINAPI *)(PVOID,DWORD,PVOID,DWORD*))GetProcAddress(hGdi, "AddFontMemResourceEx");
+		pAddFontResourceEx = (int (WINAPI *)(LPCTSTR,DWORD,PVOID))GetProcAddress(hGdi, "AddFontResourceExA");
+		pRemoveFontMemResourceEx = (BOOL (WINAPI *)(HANDLE))GetProcAddress(hGdi, "RemoveFontMemResourceEx");
+		pRemoveFontResourceEx = (BOOL (WINAPI *)(LPCTSTR,DWORD,PVOID))GetProcAddress(hGdi, "RemoveFontResourceExA");
+	}
+
+	if(HMODULE hGdi = GetModuleHandle(_T("kernel32.dll")))
+	{
+		pMoveFileEx = (BOOL (WINAPI *)(LPCTSTR, LPCTSTR, DWORD))GetProcAddress(hGdi, "MoveFileExA");
+	}
+}
+
+CFontInstaller::~CFontInstaller()
+{
+	UninstallFonts();
+}
+
+bool CFontInstaller::InstallFont(const CAtlArray<BYTE>& data)
+{
+	return InstallFont(data.GetData(), data.GetCount());
+}
+
+bool CFontInstaller::InstallFont(const void* pData, UINT len)
+{
+	return InstallFontFile(pData, len) || InstallFontMemory(pData, len);
+}
+
+void CFontInstaller::UninstallFonts()
+{
+	if(pRemoveFontMemResourceEx)
+	{
+		POSITION pos = m_fonts.GetHeadPosition();
+		while(pos) pRemoveFontMemResourceEx(m_fonts.GetNext(pos));
+		m_fonts.RemoveAll();
+	}
+
+	if(pRemoveFontResourceEx)
+	{
+		POSITION pos = m_files.GetHeadPosition();
+		while(pos)
+		{
+			CString fn = m_files.GetNext(pos);
+			pRemoveFontResourceEx(fn, FR_PRIVATE, 0);
+			if(!DeleteFile(fn) && pMoveFileEx)
+				pMoveFileEx(fn, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
+		}
+		
+		m_files.RemoveAll();
+	}
+}
+
+bool CFontInstaller::InstallFontMemory(const void* pData, UINT len)
+{
+	if(!pAddFontMemResourceEx)
+		return false;
+
+	DWORD nFonts = 0;
+	HANDLE hFont = pAddFontMemResourceEx((PVOID)pData, len, NULL, &nFonts);
+	if(hFont && nFonts > 0) m_fonts.AddTail(hFont);
+	return hFont && nFonts > 0;
+}
+
+bool CFontInstaller::InstallFontFile(const void* pData, UINT len)
+{
+	if(!pAddFontResourceEx) 
+		return false;
+
+	CFile f;
+	TCHAR path[MAX_PATH], fn[MAX_PATH];
+	if(!GetTempPath(MAX_PATH, path) || !GetTempFileName(path, _T("g_font"), 0, fn))
+		return false;
+
+	if(f.Open(fn, CFile::modeWrite))
+	{
+		f.Write(pData, len);
+		f.Close();
+
+		if(pAddFontResourceEx(fn, FR_PRIVATE, 0) > 0)
+		{
+			m_files.AddTail(fn);
+			return true;
+		}
+	}
+
+	DeleteFile(fn);
+	return false;
+}
diff --git a/vsfilter/dsutil/FontInstaller.h b/vsfilter/dsutil/FontInstaller.h
new file mode 100644
index 0000000000000000000000000000000000000000..28d7c07d4cac8766c65a6dbee1ef7b3d3c89763e
--- /dev/null
+++ b/vsfilter/dsutil/FontInstaller.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <atlcoll.h>
+
+class CFontInstaller
+{
+	HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID,DWORD,PVOID,DWORD*);
+	BOOL (WINAPI *pRemoveFontMemResourceEx)(HANDLE);
+	int (WINAPI *pAddFontResourceEx)(LPCTSTR,DWORD,PVOID);
+	BOOL (WINAPI *pRemoveFontResourceEx)(LPCTSTR,DWORD,PVOID);
+	BOOL (WINAPI *pMoveFileEx)(LPCTSTR, LPCTSTR,DWORD);
+
+	CAtlList<HANDLE> m_fonts;
+	CAtlList<CString> m_files;
+	bool InstallFontMemory(const void* pData, UINT len);
+	bool InstallFontFile(const void* pData, UINT len);
+
+public:
+	CFontInstaller();
+	virtual ~CFontInstaller();
+
+	bool InstallFont(const CAtlArray<BYTE>& data);
+	bool InstallFont(const void* pData, UINT len);	
+	void UninstallFonts();
+};
diff --git a/vsfilter/dsutil/MediaTypeEx.cpp b/vsfilter/dsutil/MediaTypeEx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..95ba9525f1533b1fe381a11845d591fc94f55309
--- /dev/null
+++ b/vsfilter/dsutil/MediaTypeEx.cpp
@@ -0,0 +1,641 @@
+#include "StdAfx.h"
+#include "DSUtil.h"
+#include "MediaTypeEx.h"
+
+#include <mmreg.h>
+#include <initguid.h>
+#include "../include/moreuuids.h"
+
+#pragma pack(push, 1)
+typedef struct
+{
+	WAVEFORMATEX Format;
+	BYTE bBigEndian;
+	BYTE bsid;
+	BYTE lfeon;
+	BYTE copyrightb;
+	BYTE nAuxBitsCode;  //  Aux bits per frame
+} DOLBYAC3WAVEFORMAT;
+#pragma pack(pop)
+
+CMediaTypeEx::CMediaTypeEx()
+{
+}
+
+CString CMediaTypeEx::ToString(IPin* pPin)
+{
+	CString packing, type, codec, dim, rate, dur;
+
+	// TODO
+
+	if(majortype == MEDIATYPE_DVD_ENCRYPTED_PACK)
+	{
+		packing = _T("Encrypted MPEG2 Pack");
+	}
+	else if(majortype == MEDIATYPE_MPEG2_PACK)
+	{
+		packing = _T("MPEG2 Pack");
+	}
+	else if(majortype == MEDIATYPE_MPEG2_PES)
+	{
+		packing = _T("MPEG2 PES");
+	}
+	
+	if(majortype == MEDIATYPE_Video)
+	{
+		type = _T("Video");
+
+		BITMAPINFOHEADER bih;
+		bool fBIH = ExtractBIH(this, &bih);
+
+		int w, h, arx, ary;
+		bool fDim = ExtractDim(this, w, h, arx, ary);
+
+		if(fBIH && bih.biCompression)
+		{
+			codec = GetVideoCodecName(subtype, bih.biCompression);
+		}
+
+		if(codec.IsEmpty())
+		{
+			if(formattype == FORMAT_MPEGVideo) codec = _T("MPEG1 Video");
+			else if(formattype == FORMAT_MPEG2_VIDEO) codec = _T("MPEG2 Video");
+			else if(formattype == FORMAT_DiracVideoInfo) codec = _T("Dirac Video");
+		}
+
+		if(fDim)
+		{
+			dim.Format(_T("%dx%d"), w, h);
+			if(w*ary != h*arx) dim.Format(_T("%s (%d:%d)"), CString(dim), arx, ary);
+		}
+
+		if(formattype == FORMAT_VideoInfo || formattype == FORMAT_MPEGVideo)
+		{
+			VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)pbFormat;
+			if(vih->AvgTimePerFrame) rate.Format(_T("%0.2ffps "), 10000000.0f / vih->AvgTimePerFrame);
+			if(vih->dwBitRate) rate.Format(_T("%s%dKbps"), CString(rate), vih->dwBitRate/1000);
+		}
+		else if(formattype == FORMAT_VideoInfo2 || formattype == FORMAT_MPEG2_VIDEO || formattype == FORMAT_DiracVideoInfo)
+		{
+			VIDEOINFOHEADER2* vih = (VIDEOINFOHEADER2*)pbFormat;
+			if(vih->AvgTimePerFrame) rate.Format(_T("%0.2ffps "), 10000000.0f / vih->AvgTimePerFrame);
+			if(vih->dwBitRate) rate.Format(_T("%s%dKbps"), CString(rate), vih->dwBitRate/1000);
+		}
+
+		rate.Trim();
+
+		if(subtype == MEDIASUBTYPE_DVD_SUBPICTURE)
+		{
+			type = _T("Subtitle");
+			codec = _T("DVD Subpicture");
+		}
+	}
+	else if(majortype == MEDIATYPE_Audio)
+	{
+		type = _T("Audio");
+
+		if(formattype == FORMAT_WaveFormatEx)
+		{
+			WAVEFORMATEX* wfe = (WAVEFORMATEX*)Format();
+
+			if(wfe->wFormatTag/* > WAVE_FORMAT_PCM && wfe->wFormatTag < WAVE_FORMAT_EXTENSIBLE
+			&& wfe->wFormatTag != WAVE_FORMAT_IEEE_FLOAT*/
+			|| subtype != GUID_NULL)
+			{
+				codec = GetAudioCodecName(subtype, wfe->wFormatTag);
+				dim.Format(_T("%dHz"), wfe->nSamplesPerSec);
+				if(wfe->nChannels == 1) dim.Format(_T("%s mono"), CString(dim));
+				else if(wfe->nChannels == 2) dim.Format(_T("%s stereo"), CString(dim));
+				else dim.Format(_T("%s %dch"), CString(dim), wfe->nChannels);
+				if(wfe->nAvgBytesPerSec) rate.Format(_T("%dKbps"), wfe->nAvgBytesPerSec*8/1000);
+			}
+		}
+		else if(formattype == FORMAT_VorbisFormat)
+		{
+			VORBISFORMAT* vf = (VORBISFORMAT*)Format();
+
+			codec = GetAudioCodecName(subtype, 0);
+			dim.Format(_T("%dHz"), vf->nSamplesPerSec);
+			if(vf->nChannels == 1) dim.Format(_T("%s mono"), CString(dim));
+			else if(vf->nChannels == 2) dim.Format(_T("%s stereo"), CString(dim));
+			else dim.Format(_T("%s %dch"), CString(dim), vf->nChannels);
+			if(vf->nAvgBitsPerSec) rate.Format(_T("%dKbps"), vf->nAvgBitsPerSec/1000);
+		}
+		else if(formattype == FORMAT_VorbisFormat2)
+		{
+			VORBISFORMAT2* vf = (VORBISFORMAT2*)Format();
+
+			codec = GetAudioCodecName(subtype, 0);
+			dim.Format(_T("%dHz"), vf->SamplesPerSec);
+			if(vf->Channels == 1) dim.Format(_T("%s mono"), CString(dim));
+			else if(vf->Channels == 2) dim.Format(_T("%s stereo"), CString(dim));
+			else dim.Format(_T("%s %dch"), CString(dim), vf->Channels);
+		}				
+	}
+	else if(majortype == MEDIATYPE_Text)
+	{
+		type = _T("Text");
+	}
+	else if(majortype == MEDIATYPE_Subtitle)
+	{
+		type = _T("Subtitle");
+		codec = GetSubtitleCodecName(subtype);
+	}
+	else
+	{
+		type = _T("Unknown");
+	}
+
+	if(CComQIPtr<IMediaSeeking> pMS = pPin)
+	{
+		REFERENCE_TIME rtDur = 0;
+		if(SUCCEEDED(pMS->GetDuration(&rtDur)) && rtDur)
+		{
+			rtDur /= 10000000;
+			int s = rtDur%60;
+			rtDur /= 60;
+			int m = rtDur%60;
+			rtDur /= 60;
+			int h = rtDur;
+			if(h) dur.Format(_T("%d:%02d:%02d"), h, m, s);
+			else if(m) dur.Format(_T("%02d:%02d"), m, s);
+			else if(s) dur.Format(_T("%ds"), s);
+		}
+	}
+
+	CString str;
+	if(!codec.IsEmpty()) str += codec + _T(" ");
+	if(!dim.IsEmpty()) str += dim + _T(" ");
+	if(!rate.IsEmpty()) str += rate + _T(" ");
+	if(!dur.IsEmpty()) str += dur + _T(" ");
+	str.Trim(_T(" ,"));
+	
+	if(!str.IsEmpty()) str = type + _T(": ") + str;
+	else str = type;
+
+	return str;
+}
+
+CString CMediaTypeEx::GetVideoCodecName(const GUID& subtype, DWORD biCompression)
+{
+	CString str;
+
+	static CAtlMap<DWORD, CString> names;
+
+	if(names.IsEmpty())
+	{
+		names['WMV1'] = _T("Windows Media Video 7");
+		names['WMV2'] = _T("Windows Media Video 8");
+		names['WMV3'] = _T("Windows Media Video 9");
+		names['DIV3'] = _T("DivX 3");
+		names['DX50'] = _T("DivX 5");
+		names['MP4V'] = _T("MPEG4 Video");
+		names['AVC1'] = _T("MPEG4 Video (H264)");
+		names['H264'] = _T("MPEG4 Video (H264)");
+		names['RV10'] = _T("RealVideo 1");
+		names['RV20'] = _T("RealVideo 2");
+		names['RV30'] = _T("RealVideo 3");
+		names['RV40'] = _T("RealVideo 4");
+		names['FLV1'] = _T("Flash Video 1");
+		// names[''] = _T("");
+	}
+
+	if(biCompression)
+	{
+		BYTE* b = (BYTE*)&biCompression;
+
+		for(int i = 0; i < 4; i++)
+			if(b[i] >= 'a' && b[i] <= 'z') 
+				b[i] = toupper(b[i]);
+
+		if(!names.Lookup(MAKEFOURCC(b[3], b[2], b[1], b[0]), str))
+		{
+			if(subtype == MEDIASUBTYPE_DiracVideo) str = _T("Dirac Video");
+			// else if(subtype == ) str = _T("");
+			else if(biCompression < 256) str.Format(_T("%d"), biCompression);
+			else str.Format(_T("%4.4hs"), &biCompression);
+		}
+	}
+
+	return str;
+}
+
+CString CMediaTypeEx::GetAudioCodecName(const GUID& subtype, WORD wFormatTag)
+{
+	CString str;
+
+	static CAtlMap<WORD, CString> names;
+
+	if(names.IsEmpty())
+	{
+		names[WAVE_FORMAT_PCM] = _T("PCM");
+		names[WAVE_FORMAT_EXTENSIBLE] = _T("WAVE_FORMAT_EXTENSIBLE");
+		names[WAVE_FORMAT_IEEE_FLOAT] = _T("IEEE Float");
+		names[WAVE_FORMAT_ADPCM] = _T("MS ADPCM");
+		names[WAVE_FORMAT_ALAW] = _T("aLaw");
+		names[WAVE_FORMAT_MULAW] = _T("muLaw");
+		names[WAVE_FORMAT_DRM] = _T("DRM");
+		names[WAVE_FORMAT_OKI_ADPCM] = _T("OKI ADPCM");
+		names[WAVE_FORMAT_DVI_ADPCM] = _T("DVI ADPCM");
+		names[WAVE_FORMAT_IMA_ADPCM] = _T("IMA ADPCM");
+		names[WAVE_FORMAT_MEDIASPACE_ADPCM] = _T("Mediaspace ADPCM");
+		names[WAVE_FORMAT_SIERRA_ADPCM] = _T("Sierra ADPCM");
+		names[WAVE_FORMAT_G723_ADPCM] = _T("G723 ADPCM");
+		names[WAVE_FORMAT_DIALOGIC_OKI_ADPCM] = _T("Dialogic OKI ADPCM");
+		names[WAVE_FORMAT_MEDIAVISION_ADPCM] = _T("Media Vision ADPCM");
+		names[WAVE_FORMAT_YAMAHA_ADPCM] = _T("Yamaha ADPCM");
+		names[WAVE_FORMAT_DSPGROUP_TRUESPEECH] = _T("DSP Group Truespeech");
+		names[WAVE_FORMAT_DOLBY_AC2] = _T("Dolby AC2");
+		names[WAVE_FORMAT_GSM610] = _T("GSM610");
+		names[WAVE_FORMAT_MSNAUDIO] = _T("MSN Audio");
+		names[WAVE_FORMAT_ANTEX_ADPCME] = _T("Antex ADPCME");
+		names[WAVE_FORMAT_CS_IMAADPCM] = _T("Crystal Semiconductor IMA ADPCM");
+		names[WAVE_FORMAT_ROCKWELL_ADPCM] = _T("Rockwell ADPCM");
+		names[WAVE_FORMAT_ROCKWELL_DIGITALK] = _T("Rockwell Digitalk");
+		names[WAVE_FORMAT_G721_ADPCM] = _T("G721");
+		names[WAVE_FORMAT_G728_CELP] = _T("G728");
+		names[WAVE_FORMAT_MSG723] = _T("MSG723");
+		names[WAVE_FORMAT_MPEG] = _T("MPEG Audio");
+		names[WAVE_FORMAT_MPEGLAYER3] = _T("MPEG Audio Layer 3");
+		names[WAVE_FORMAT_LUCENT_G723] = _T("Lucent G723");
+		names[WAVE_FORMAT_VOXWARE] = _T("Voxware");
+		names[WAVE_FORMAT_G726_ADPCM] = _T("G726");
+		names[WAVE_FORMAT_G722_ADPCM] = _T("G722");
+		names[WAVE_FORMAT_G729A] = _T("G729A");
+		names[WAVE_FORMAT_MEDIASONIC_G723] = _T("MediaSonic G723");
+		names[WAVE_FORMAT_ZYXEL_ADPCM] = _T("ZyXEL ADPCM");
+		names[WAVE_FORMAT_RHETOREX_ADPCM] = _T("Rhetorex ADPCM");
+		names[WAVE_FORMAT_VIVO_G723] = _T("Vivo G723");
+		names[WAVE_FORMAT_VIVO_SIREN] = _T("Vivo Siren");
+		names[WAVE_FORMAT_DIGITAL_G723] = _T("Digital G723");
+		names[WAVE_FORMAT_SANYO_LD_ADPCM] = _T("Sanyo LD ADPCM");
+		names[WAVE_FORMAT_CREATIVE_ADPCM] = _T("Creative ADPCM");
+		names[WAVE_FORMAT_CREATIVE_FASTSPEECH8] = _T("Creative Fastspeech 8");
+		names[WAVE_FORMAT_CREATIVE_FASTSPEECH10] = _T("Creative Fastspeech 10");
+		names[WAVE_FORMAT_UHER_ADPCM] = _T("UHER ADPCM");
+		names[WAVE_FORMAT_DOLBY_AC3] = _T("Dolby AC3");
+		names[WAVE_FORMAT_DVD_DTS] = _T("DTS");
+		names[WAVE_FORMAT_AAC] = _T("AAC");
+		names[WAVE_FORMAT_FLAC] = _T("FLAC");
+		names[WAVE_FORMAT_TTA1] = _T("TTA");
+		names[WAVE_FORMAT_14_4] = _T("RealAudio 14.4");
+		names[WAVE_FORMAT_28_8] = _T("RealAudio 28.8");
+		names[WAVE_FORMAT_ATRC] = _T("RealAudio ATRC");
+		names[WAVE_FORMAT_COOK] = _T("RealAudio COOK");
+		names[WAVE_FORMAT_DNET] = _T("RealAudio DNET");
+		names[WAVE_FORMAT_RAAC] = _T("RealAudio RAAC");
+		names[WAVE_FORMAT_RACP] = _T("RealAudio RACP");
+		names[WAVE_FORMAT_SIPR] = _T("RealAudio SIPR");
+		names[WAVE_FORMAT_PS2_PCM] = _T("PS2 PCM");
+		names[WAVE_FORMAT_PS2_ADPCM] = _T("PS2 ADPCM");
+		names[0x0160] = _T("Windows Media Audio");
+		names[0x0161] = _T("Windows Media Audio");
+		names[0x0162] = _T("Windows Media Audio");
+		names[0x0163] = _T("Windows Media Audio");
+		// names[] = _T("");
+	}
+
+	if(!names.Lookup(wFormatTag, str))
+	{
+		if(subtype == MEDIASUBTYPE_Vorbis) str = _T("Vorbis (deprecated)");
+		else if(subtype == MEDIASUBTYPE_Vorbis2) str = _T("Vorbis");
+		else if(subtype == MEDIASUBTYPE_MP4A) str = _T("MPEG4 Audio");
+		else if(subtype == MEDIASUBTYPE_FLAC_FRAMED) str = _T("FLAC (framed)");
+		else if(subtype == MEDIASUBTYPE_DOLBY_AC3) str += _T("Dolby AC3");
+		else if(subtype == MEDIASUBTYPE_DTS) str += _T("DTS");
+		// else if(subtype == ) str = _T("");
+		else str.Format(_T("0x%04x"), wFormatTag);
+	}
+
+	if(wFormatTag == WAVE_FORMAT_PCM)
+	{
+		if(subtype == MEDIASUBTYPE_DOLBY_AC3) str += _T(" (AC3)");
+		else if(subtype == MEDIASUBTYPE_DTS) str += _T(" (DTS)");
+	}
+
+	return str;
+}
+
+CString CMediaTypeEx::GetSubtitleCodecName(const GUID& subtype)
+{
+	CString str;
+
+	static CAtlMap<GUID, CString> names;
+
+	if(names.IsEmpty())
+	{
+		names[MEDIASUBTYPE_UTF8] = _T("UTF-8");
+		names[MEDIASUBTYPE_SSA] = _T("SubStation Alpha");
+		names[MEDIASUBTYPE_ASS] = _T("Advanced SubStation Alpha");
+		names[MEDIASUBTYPE_ASS2] = _T("Advanced SubStation Alpha");
+		names[MEDIASUBTYPE_SSF] = _T("Stuctured Subtitle Format");
+		names[MEDIASUBTYPE_USF] = _T("Universal Subtitle Format");
+		names[MEDIASUBTYPE_VOBSUB] = _T("VobSub");
+		// names[''] = _T("");
+	}
+
+	if(names.Lookup(subtype, str))
+	{
+
+	}
+
+	return str;
+}
+
+void CMediaTypeEx::Dump(CAtlList<CString>& sl)
+{
+	CString str;
+
+	sl.RemoveAll();
+
+	int fmtsize = 0;
+
+	CString major = CStringFromGUID(majortype);
+	CString sub = CStringFromGUID(subtype);
+	CString format = CStringFromGUID(formattype);
+
+	sl.AddTail(ToString() + _T("\n"));	
+
+	sl.AddTail(_T("AM_MEDIA_TYPE: "));
+	str.Format(_T("majortype: %s %s"), CString(GuidNames[majortype]), major);
+	sl.AddTail(str);
+	str.Format(_T("subtype: %s %s"), CString(GuidNames[subtype]), sub);
+	sl.AddTail(str);
+	str.Format(_T("formattype: %s %s"), CString(GuidNames[formattype]), format);
+	sl.AddTail(str);
+	str.Format(_T("bFixedSizeSamples: %d"), bFixedSizeSamples);
+	sl.AddTail(str);
+	str.Format(_T("bTemporalCompression: %d"), bTemporalCompression);
+	sl.AddTail(str);
+	str.Format(_T("lSampleSize: %d"), lSampleSize);
+	sl.AddTail(str);
+	str.Format(_T("cbFormat: %d"), cbFormat);
+	sl.AddTail(str);
+
+	sl.AddTail(_T(""));
+
+	if(formattype == FORMAT_VideoInfo || formattype == FORMAT_VideoInfo2
+	|| formattype == FORMAT_MPEGVideo || formattype == FORMAT_MPEG2_VIDEO)
+	{
+		fmtsize = 
+			formattype == FORMAT_VideoInfo ? sizeof(VIDEOINFOHEADER) :
+			formattype == FORMAT_VideoInfo2 ? sizeof(VIDEOINFOHEADER2) :
+			formattype == FORMAT_MPEGVideo ? sizeof(MPEG1VIDEOINFO)-1 :
+			formattype == FORMAT_MPEG2_VIDEO ? sizeof(MPEG2VIDEOINFO)-4 :
+			0;
+
+		VIDEOINFOHEADER& vih = *(VIDEOINFOHEADER*)pbFormat;
+		BITMAPINFOHEADER* bih = &vih.bmiHeader;
+
+		sl.AddTail(_T("VIDEOINFOHEADER:"));
+		str.Format(_T("rcSource: (%d,%d)-(%d,%d)"), vih.rcSource);
+		sl.AddTail(str);
+		str.Format(_T("rcTarget: (%d,%d)-(%d,%d)"), vih.rcTarget);
+		sl.AddTail(str);
+		str.Format(_T("dwBitRate: %d"), vih.dwBitRate);
+		sl.AddTail(str);
+		str.Format(_T("dwBitErrorRate: %d"), vih.dwBitErrorRate);
+		sl.AddTail(str);
+		str.Format(_T("AvgTimePerFrame: %I64d"), vih.AvgTimePerFrame);
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+
+		if(formattype == FORMAT_VideoInfo2 || formattype == FORMAT_MPEG2_VIDEO)
+		{
+			VIDEOINFOHEADER2& vih = *(VIDEOINFOHEADER2*)pbFormat;
+			bih = &vih.bmiHeader;
+
+			sl.AddTail(_T("VIDEOINFOHEADER2:"));
+			str.Format(_T("dwInterlaceFlags: 0x%08x"), vih.dwInterlaceFlags);
+			sl.AddTail(str);
+			str.Format(_T("dwCopyProtectFlags: 0x%08x"), vih.dwCopyProtectFlags);
+			sl.AddTail(str);
+			str.Format(_T("dwPictAspectRatioX: %d"), vih.dwPictAspectRatioX);
+			sl.AddTail(str);
+			str.Format(_T("dwPictAspectRatioY: %d"), vih.dwPictAspectRatioY);
+			sl.AddTail(str);
+			str.Format(_T("dwControlFlags: 0x%08x"), vih.dwControlFlags);
+			sl.AddTail(str);
+			str.Format(_T("dwReserved2: 0x%08x"), vih.dwReserved2);
+			sl.AddTail(str);
+
+			sl.AddTail(_T(""));
+		}
+
+		if(formattype == FORMAT_MPEGVideo)
+		{
+			MPEG1VIDEOINFO& mvih = *(MPEG1VIDEOINFO*)pbFormat;
+
+			sl.AddTail(_T("MPEG1VIDEOINFO:"));
+			str.Format(_T("dwStartTimeCode: %d"), mvih.dwStartTimeCode);
+			sl.AddTail(str);
+			str.Format(_T("cbSequenceHeader: %d"), mvih.cbSequenceHeader);
+			sl.AddTail(str);
+
+			sl.AddTail(_T(""));
+		}
+		else if(formattype == FORMAT_MPEG2_VIDEO)
+		{
+			MPEG2VIDEOINFO& mvih = *(MPEG2VIDEOINFO*)pbFormat;
+
+			sl.AddTail(_T("MPEG2VIDEOINFO:"));
+			str.Format(_T("dwStartTimeCode: %d"), mvih.dwStartTimeCode);
+			sl.AddTail(str);
+			str.Format(_T("cbSequenceHeader: %d"), mvih.cbSequenceHeader);
+			sl.AddTail(str);
+			str.Format(_T("dwProfile: 0x%08x"), mvih.dwProfile);
+			sl.AddTail(str);
+			str.Format(_T("dwLevel: 0x%08x"), mvih.dwLevel);
+			sl.AddTail(str);
+			str.Format(_T("dwFlags: 0x%08x"), mvih.dwFlags);
+			sl.AddTail(str);
+
+			sl.AddTail(_T(""));
+		}
+
+		sl.AddTail(_T("BITMAPINFOHEADER:"));
+		str.Format(_T("biSize: %d"), bih->biSize);
+		sl.AddTail(str);
+		str.Format(_T("biWidth: %d"), bih->biWidth);
+		sl.AddTail(str);
+		str.Format(_T("biHeight: %d"), bih->biHeight);
+		sl.AddTail(str);
+		str.Format(_T("biPlanes: %d"), bih->biPlanes);
+		sl.AddTail(str);
+		str.Format(_T("biBitCount: %d"), bih->biBitCount);
+		sl.AddTail(str);
+		if(bih->biCompression < 256) str.Format(_T("biCompression: %d"), bih->biCompression);
+		else str.Format(_T("biCompression: %4.4hs"), &bih->biCompression);
+		sl.AddTail(str);
+		str.Format(_T("biSizeImage: %d"), bih->biSizeImage);
+		sl.AddTail(str);
+		str.Format(_T("biXPelsPerMeter: %d"), bih->biXPelsPerMeter);
+		sl.AddTail(str);
+		str.Format(_T("biYPelsPerMeter: %d"), bih->biYPelsPerMeter);
+		sl.AddTail(str);
+		str.Format(_T("biClrUsed: %d"), bih->biClrUsed);
+		sl.AddTail(str);
+		str.Format(_T("biClrImportant: %d"), bih->biClrImportant);
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+    }
+	else if(formattype == FORMAT_WaveFormatEx)
+	{
+		fmtsize = sizeof(WAVEFORMATEX);
+
+		WAVEFORMATEX& wfe = *(WAVEFORMATEX*)pbFormat;
+
+		sl.AddTail(_T("WAVEFORMATEX:"));
+		str.Format(_T("wFormatTag: 0x%04x"), wfe.wFormatTag);
+		sl.AddTail(str);
+		str.Format(_T("nChannels: %d"), wfe.nChannels);
+		sl.AddTail(str);
+		str.Format(_T("nSamplesPerSec: %d"), wfe.nSamplesPerSec);
+		sl.AddTail(str);
+		str.Format(_T("nAvgBytesPerSec: %d"), wfe.nAvgBytesPerSec);
+		sl.AddTail(str);
+		str.Format(_T("nBlockAlign: %d"), wfe.nBlockAlign);
+		sl.AddTail(str);
+		str.Format(_T("wBitsPerSample: %d"), wfe.wBitsPerSample);
+		sl.AddTail(str);
+		str.Format(_T("cbSize: %d (extra bytes)"), wfe.cbSize);
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+
+		if(wfe.wFormatTag != WAVE_FORMAT_PCM && wfe.cbSize > 0)
+		{
+			if(wfe.wFormatTag == WAVE_FORMAT_EXTENSIBLE && wfe.cbSize == sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX))
+			{
+				fmtsize = sizeof(WAVEFORMATEXTENSIBLE);
+
+				WAVEFORMATEXTENSIBLE& wfe = *(WAVEFORMATEXTENSIBLE*)pbFormat;
+
+				sl.AddTail(_T("WAVEFORMATEXTENSIBLE:"));
+				if(wfe.Format.wBitsPerSample != 0) str.Format(_T("wValidBitsPerSample: %d"), wfe.Samples.wValidBitsPerSample);
+				else str.Format(_T("wSamplesPerBlock: %d"), wfe.Samples.wSamplesPerBlock);
+				sl.AddTail(str);
+				str.Format(_T("dwChannelMask: 0x%08x"), wfe.dwChannelMask);
+				sl.AddTail(str);
+				str.Format(_T("SubFormat: %s"), CStringFromGUID(wfe.SubFormat));
+				sl.AddTail(str);
+
+				sl.AddTail(_T(""));
+			}
+			else if(wfe.wFormatTag == WAVE_FORMAT_DOLBY_AC3 && wfe.cbSize == sizeof(DOLBYAC3WAVEFORMAT)-sizeof(WAVEFORMATEX))
+			{
+				fmtsize = sizeof(DOLBYAC3WAVEFORMAT);
+
+				DOLBYAC3WAVEFORMAT& wfe = *(DOLBYAC3WAVEFORMAT*)pbFormat;
+
+				sl.AddTail(_T("DOLBYAC3WAVEFORMAT:"));
+				str.Format(_T("bBigEndian: %d"), wfe.bBigEndian);
+				sl.AddTail(str);
+				str.Format(_T("bsid: %d"), wfe.bsid);
+				sl.AddTail(str);
+				str.Format(_T("lfeon: %d"), wfe.lfeon);
+				sl.AddTail(str);
+				str.Format(_T("copyrightb: %d"), wfe.copyrightb);
+				sl.AddTail(str);
+				str.Format(_T("nAuxBitsCode: %d"), wfe.nAuxBitsCode);
+				sl.AddTail(str);
+
+				sl.AddTail(_T(""));
+			}
+		}
+	}
+	else if(formattype == FORMAT_VorbisFormat)
+	{
+		fmtsize = sizeof(VORBISFORMAT);
+
+		VORBISFORMAT& vf = *(VORBISFORMAT*)pbFormat;
+
+		sl.AddTail(_T("VORBISFORMAT:"));
+		str.Format(_T("nChannels: %d"), vf.nChannels);
+		sl.AddTail(str);
+		str.Format(_T("nSamplesPerSec: %d"), vf.nSamplesPerSec);
+		sl.AddTail(str);
+		str.Format(_T("nMinBitsPerSec: %d"), vf.nMinBitsPerSec);
+		sl.AddTail(str);
+		str.Format(_T("nAvgBitsPerSec: %d"), vf.nAvgBitsPerSec);
+		sl.AddTail(str);
+		str.Format(_T("nMaxBitsPerSec: %d"), vf.nMaxBitsPerSec);
+		sl.AddTail(str);
+		str.Format(_T("fQuality: %.3f"), vf.fQuality);
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+	}
+	else if(formattype == FORMAT_VorbisFormat2)
+	{
+		fmtsize = sizeof(VORBISFORMAT2);
+
+		VORBISFORMAT2& vf = *(VORBISFORMAT2*)pbFormat;
+
+		sl.AddTail(_T("VORBISFORMAT:"));
+		str.Format(_T("Channels: %d"), vf.Channels);
+		sl.AddTail(str);
+		str.Format(_T("SamplesPerSec: %d"), vf.SamplesPerSec);
+		sl.AddTail(str);
+		str.Format(_T("BitsPerSample: %d"), vf.BitsPerSample);
+		sl.AddTail(str);
+		str.Format(_T("HeaderSize: {%d, %d, %d}"), vf.HeaderSize[0], vf.HeaderSize[1], vf.HeaderSize[2]);
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+	}
+	else if(formattype == FORMAT_SubtitleInfo)
+	{
+		fmtsize = sizeof(SUBTITLEINFO);
+
+		SUBTITLEINFO& si = *(SUBTITLEINFO*)pbFormat;
+
+		sl.AddTail(_T("SUBTITLEINFO:"));
+		str.Format(_T("dwOffset: %d"), si.dwOffset);
+		sl.AddTail(str);
+		str.Format(_T("IsoLang: %s"), CString(CStringA(si.IsoLang, sizeof(si.IsoLang)-1)));
+		sl.AddTail(str);
+		str.Format(_T("TrackName: %s"), CString(CStringW(si.TrackName, sizeof(si.TrackName)-1)));
+		sl.AddTail(str);
+
+		sl.AddTail(_T(""));
+	}
+
+	if(cbFormat > 0)
+	{
+		sl.AddTail(_T("pbFormat:"));
+
+		for(int i = 0, j = (cbFormat + 15) & ~15; i < j; i += 16)
+		{
+			str.Format(_T("%04x:"), i);
+
+			for(int k = i, l = min(i + 16, cbFormat); k < l; k++)
+			{
+				CString byte;
+				byte.Format(_T("%c%02x"), fmtsize > 0 && fmtsize == k ? '|' : ' ', pbFormat[k]);
+				str += byte;
+			}
+
+			for(int k = min(i + 16, cbFormat), l = i + 16; k < l; k++)
+			{
+				str += _T("   ");
+			}
+
+			str += ' ';
+
+			for(int k = i, l = min(i + 16, cbFormat); k < l; k++)
+			{
+				unsigned char c = (unsigned char)pbFormat[k];
+				CStringA ch;
+				ch.Format("%c", c >= 0x20 ? c : '.');
+				str += ch;
+			}
+
+			sl.AddTail(str);
+		}
+
+		sl.AddTail(_T(""));
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/dsutil/MediaTypeEx.h b/vsfilter/dsutil/MediaTypeEx.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4121b4d26e938cc681bb4b7eef09b07d20162c8
--- /dev/null
+++ b/vsfilter/dsutil/MediaTypeEx.h
@@ -0,0 +1,16 @@
+#pragma once
+
+class CMediaTypeEx : public CMediaType
+{
+public:
+	CMediaTypeEx();
+	CMediaTypeEx(const CMediaType& mt) {CMediaType::operator = (mt);}
+
+	CString ToString(IPin* pPin = NULL);
+
+	static CString GetVideoCodecName(const GUID& subtype, DWORD biCompression);
+	static CString GetAudioCodecName(const GUID& subtype, WORD wFormatTag);
+	static CString GetSubtitleCodecName(const GUID& subtype);
+
+	void Dump(CAtlList<CString>& sl);
+};
diff --git a/vsfilter/dsutil/MediaTypes.cpp b/vsfilter/dsutil/MediaTypes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4bdff5867c61060d2e3d291581f1cf91fb3904e2
--- /dev/null
+++ b/vsfilter/dsutil/MediaTypes.cpp
@@ -0,0 +1,381 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <initguid.h>
+#include "../include/moreuuids.h"
+#include "MediaTypes.h"
+#include "DSUtil.h"
+
+#define VIH_NORMAL (sizeof(VIDEOINFOHEADER))
+#define VIH_BITFIELDS (sizeof(VIDEOINFOHEADER)+3*sizeof(RGBQUAD))
+#define VIH2_NORMAL (sizeof(VIDEOINFOHEADER2))
+#define VIH2_BITFIELDS (sizeof(VIDEOINFOHEADER2)+3*sizeof(RGBQUAD))
+#define BIH_SIZE (sizeof(BITMAPINFOHEADER))
+
+VIH vihs[] =
+{
+	// YUY2
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, mmioFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_YUY2												// subtype
+	},
+	// YV12
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('Y','V','1','2'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_YV12												// subtype
+	},
+	// I420
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('I','4','2','0'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_I420												// subtype
+	},
+	// IYUV
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('I','Y','U','V'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_IYUV												// subtype
+	},
+	// 8888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_RGB32												// subtype
+	},
+	// 8888 bitf 
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB32												// subtype
+	},
+	// A888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_ARGB32											// subtype
+	},
+	// A888 bitf (I'm not sure if this exist...)
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH_BITFIELDS,													// size
+		&MEDIASUBTYPE_ARGB32											// subtype
+	},
+	// 888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 24, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_RGB24												// subtype
+	},
+	// 888 bitf 
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 24, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB24												// subtype
+	},
+	// 565 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_RGB565											// subtype
+	},
+	// 565 bitf
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xF800, 0x07E0, 0x001F},										// mask[3]
+		VIH_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB565											// subtype
+	},
+	// 555 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH_NORMAL,														// size
+		&MEDIASUBTYPE_RGB555											// subtype
+	},
+	// 555 bitf
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0x7C00, 0x03E0, 0x001F},										// mask[3]
+		VIH_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB555											// subtype
+	},
+};
+
+VIH2 vih2s[] =
+{
+	// YUY2
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, mmioFOURCC('Y','U','Y','2'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_YUY2												// subtype
+	},
+	// YV12
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('Y','V','1','2'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_YV12												// subtype
+	},
+	// I420
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('I','4','2','0'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_I420												// subtype
+	},
+	// IYUV
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 12, mmioFOURCC('I','Y','U','V'), 0, 0, 0, 0, 0}		// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_IYUV												// subtype
+	},
+	// 8888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_RGB32												// subtype
+	},
+	// 8888 bitf 
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH2_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB32												// subtype
+	},
+	// A888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_ARGB32											// subtype
+	},
+	// A888 bitf (I'm not sure if this exist...)
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 32, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH2_BITFIELDS,													// size
+		&MEDIASUBTYPE_ARGB32											// subtype
+	},
+	// 888 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 24, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_RGB24												// subtype
+	},
+	// 888 bitf 
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 24, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xFF0000, 0x00FF00, 0x0000FF},									// mask[3]
+		VIH2_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB24												// subtype
+	},
+	// 565 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_RGB565											// subtype
+	},
+	// 565 bitf
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0xF800, 0x07E0, 0x001F},										// mask[3]
+		VIH2_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB565											// subtype
+	},
+	// 555 normal
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_RGB, 0, 0, 0, 0, 0}			// bmiHeader
+		}, 
+		{0, 0, 0},														// mask[3]
+		VIH2_NORMAL,													// size
+		&MEDIASUBTYPE_RGB555											// subtype
+	},
+	// 555 bitf
+	{
+		{					
+			{0, 0, 0, 0}, {0, 0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			{BIH_SIZE, 0, 0, 1, 16, BI_BITFIELDS, 0, 0, 0, 0, 0}	// bmiHeader
+		}, 
+		{0x7C00, 0x03E0, 0x001F},										// mask[3]
+		VIH2_BITFIELDS,													// size
+		&MEDIASUBTYPE_RGB555											// subtype
+	},
+};
+
+int VIHSIZE = countof(vihs);
+
+CString VIH2String(int i)
+{
+	CString ret = CString(GuidNames[*vihs[i].subtype]);
+	if(!ret.Left(13).CompareNoCase(_T("MEDIASUBTYPE_"))) ret = ret.Mid(13);
+	if(vihs[i].vih.bmiHeader.biCompression == 3) ret += _T(" BITF");
+	if(*vihs[i].subtype == MEDIASUBTYPE_I420) ret = _T("I420"); // FIXME
+	return(ret);
+}
+
+CString Subtype2String(const GUID& subtype)
+{
+	CString ret = CString(GuidNames[subtype]);
+	if(!ret.Left(13).CompareNoCase(_T("MEDIASUBTYPE_"))) ret = ret.Mid(13);
+	if(subtype == MEDIASUBTYPE_I420) ret = _T("I420"); // FIXME
+	return(ret);
+}
+
+void CorrectMediaType(AM_MEDIA_TYPE* pmt)
+{
+	if(!pmt) return;
+
+	CMediaType mt(*pmt);
+
+	if(mt.formattype == FORMAT_VideoInfo)
+	{
+		VIDEOINFOHEADER* vih = (VIDEOINFOHEADER*)mt.pbFormat;
+
+		for(int i = 0; i < VIHSIZE; i++)
+		{
+			if(mt.subtype == *vihs[i].subtype
+			&& vih->bmiHeader.biCompression == vihs[i].vih.bmiHeader.biCompression)
+			{
+				mt.AllocFormatBuffer(vihs[i].size);
+				memcpy(mt.pbFormat, &vihs[i], vihs[i].size);
+				memcpy(mt.pbFormat, pmt->pbFormat, sizeof(VIDEOINFOHEADER));
+				break;
+			}
+		}
+	}
+	else if(mt.formattype == FORMAT_VideoInfo2)
+	{
+		VIDEOINFOHEADER2* vih2 = (VIDEOINFOHEADER2*)mt.pbFormat;
+
+		for(int i = 0; i < VIHSIZE; i++)
+		{
+			if(mt.subtype == *vih2s[i].subtype
+			&& vih2->bmiHeader.biCompression == vih2s[i].vih.bmiHeader.biCompression)
+			{
+				mt.AllocFormatBuffer(vih2s[i].size);
+				memcpy(mt.pbFormat, &vih2s[i], vih2s[i].size);
+				memcpy(mt.pbFormat, pmt->pbFormat, sizeof(VIDEOINFOHEADER2));
+				break;
+			}
+		}
+	}
+
+	CopyMediaType(pmt, &mt);
+}
diff --git a/vsfilter/dsutil/MediaTypes.h b/vsfilter/dsutil/MediaTypes.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ed2a5014e66ef43a7de27b6d4f909602976a338
--- /dev/null
+++ b/vsfilter/dsutil/MediaTypes.h
@@ -0,0 +1,47 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#pragma pack(1)
+struct VIH
+{
+	VIDEOINFOHEADER vih;
+	UINT mask[3];
+	int size;
+	const GUID* subtype;
+};
+struct VIH2
+{
+	VIDEOINFOHEADER2 vih;
+	UINT mask[3];
+	int size;
+	const GUID* subtype;
+};
+#pragma pack()
+
+extern VIH vihs[];
+extern VIH2 vih2s[];
+
+extern int VIHSIZE;
+
+extern CString VIH2String(int i), Subtype2String(const GUID& subtype);
+extern void CorrectMediaType(AM_MEDIA_TYPE* pmt);
diff --git a/vsfilter/dsutil/NullRenderers.cpp b/vsfilter/dsutil/NullRenderers.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5f819639bfcdbf24470588880021b6409339ecb8
--- /dev/null
+++ b/vsfilter/dsutil/NullRenderers.cpp
@@ -0,0 +1,158 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "StdAfx.h"
+#include "NullRenderers.h"
+#include "../include/moreuuids.h"
+
+//
+// CNullRenderer
+//
+
+CNullRenderer::CNullRenderer(REFCLSID clsid, TCHAR* pName, LPUNKNOWN pUnk, HRESULT* phr) 
+	: CBaseRenderer(clsid, pName, pUnk, phr)
+{
+}
+
+//
+// CNullVideoRenderer
+//
+
+CNullVideoRenderer::CNullVideoRenderer(LPUNKNOWN pUnk, HRESULT* phr) 
+	: CNullRenderer(__uuidof(this), NAME("Null Video Renderer"), pUnk, phr)
+{
+}
+
+HRESULT CNullVideoRenderer::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Video
+		|| pmt->subtype == MEDIASUBTYPE_MPEG2_VIDEO
+		? S_OK
+		: E_FAIL;
+}
+
+//
+// CNullUVideoRenderer
+//
+
+CNullUVideoRenderer::CNullUVideoRenderer(LPUNKNOWN pUnk, HRESULT* phr) 
+	: CNullRenderer(__uuidof(this), NAME("Null Video Renderer (Uncompressed)"), pUnk, phr)
+{
+}
+
+HRESULT CNullUVideoRenderer::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Video
+		&& (pmt->subtype == MEDIASUBTYPE_YV12
+		|| pmt->subtype == MEDIASUBTYPE_I420
+		|| pmt->subtype == MEDIASUBTYPE_YUYV
+		|| pmt->subtype == MEDIASUBTYPE_IYUV
+		|| pmt->subtype == MEDIASUBTYPE_YVU9
+		|| pmt->subtype == MEDIASUBTYPE_Y411
+		|| pmt->subtype == MEDIASUBTYPE_Y41P
+		|| pmt->subtype == MEDIASUBTYPE_YUY2
+		|| pmt->subtype == MEDIASUBTYPE_YVYU
+		|| pmt->subtype == MEDIASUBTYPE_UYVY
+		|| pmt->subtype == MEDIASUBTYPE_Y211
+		|| pmt->subtype == MEDIASUBTYPE_RGB1
+		|| pmt->subtype == MEDIASUBTYPE_RGB4
+		|| pmt->subtype == MEDIASUBTYPE_RGB8
+		|| pmt->subtype == MEDIASUBTYPE_RGB565
+		|| pmt->subtype == MEDIASUBTYPE_RGB555
+		|| pmt->subtype == MEDIASUBTYPE_RGB24
+		|| pmt->subtype == MEDIASUBTYPE_RGB32
+		|| pmt->subtype == MEDIASUBTYPE_ARGB1555
+		|| pmt->subtype == MEDIASUBTYPE_ARGB4444
+		|| pmt->subtype == MEDIASUBTYPE_ARGB32
+		|| pmt->subtype == MEDIASUBTYPE_A2R10G10B10
+		|| pmt->subtype == MEDIASUBTYPE_A2B10G10R10)
+		? S_OK
+		: E_FAIL;
+}
+
+//
+// CNullAudioRenderer
+//
+
+CNullAudioRenderer::CNullAudioRenderer(LPUNKNOWN pUnk, HRESULT* phr) 
+	: CNullRenderer(__uuidof(this), NAME("Null Audio Renderer"), pUnk, phr)
+{
+}
+
+HRESULT CNullAudioRenderer::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Audio
+		|| pmt->majortype == MEDIATYPE_Midi
+		|| pmt->subtype == MEDIASUBTYPE_MPEG2_AUDIO
+		|| pmt->subtype == MEDIASUBTYPE_DOLBY_AC3
+		|| pmt->subtype == MEDIASUBTYPE_DVD_LPCM_AUDIO
+		|| pmt->subtype == MEDIASUBTYPE_DTS
+		|| pmt->subtype == MEDIASUBTYPE_SDDS
+		|| pmt->subtype == MEDIASUBTYPE_MPEG1AudioPayload
+		|| pmt->subtype == MEDIASUBTYPE_MPEG1Audio
+		|| pmt->subtype == MEDIASUBTYPE_MPEG1Audio
+		? S_OK
+		: E_FAIL;
+}
+
+//
+// CNullUAudioRenderer
+//
+
+CNullUAudioRenderer::CNullUAudioRenderer(LPUNKNOWN pUnk, HRESULT* phr) 
+	: CNullRenderer(__uuidof(this), NAME("Null Audio Renderer (Uncompressed)"), pUnk, phr)
+{
+}
+
+HRESULT CNullUAudioRenderer::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Audio
+		&& (pmt->subtype == MEDIASUBTYPE_PCM
+		|| pmt->subtype == MEDIASUBTYPE_IEEE_FLOAT
+		|| pmt->subtype == MEDIASUBTYPE_DRM_Audio
+		|| pmt->subtype == MEDIASUBTYPE_DOLBY_AC3_SPDIF
+		|| pmt->subtype == MEDIASUBTYPE_RAW_SPORT
+		|| pmt->subtype == MEDIASUBTYPE_SPDIF_TAG_241h)
+		? S_OK
+		: E_FAIL;
+}
+
+//
+// CNullTextRenderer
+//
+
+HRESULT CNullTextRenderer::CTextInputPin::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Text
+		|| pmt->majortype == MEDIATYPE_ScriptCommand
+		|| pmt->majortype == MEDIATYPE_Subtitle 
+		|| pmt->subtype == MEDIASUBTYPE_DVD_SUBPICTURE 
+		|| pmt->subtype == MEDIASUBTYPE_CVD_SUBPICTURE 
+		|| pmt->subtype == MEDIASUBTYPE_SVCD_SUBPICTURE 
+		? S_OK 
+		: E_FAIL;
+}
+
+CNullTextRenderer::CNullTextRenderer(LPUNKNOWN pUnk, HRESULT* phr)
+	: CBaseFilter(NAME("CNullTextRenderer"), pUnk, this, __uuidof(this), phr) 
+{
+	m_pInput.Attach(new CTextInputPin(this, this, phr));
+}
diff --git a/vsfilter/dsutil/NullRenderers.h b/vsfilter/dsutil/NullRenderers.h
new file mode 100644
index 0000000000000000000000000000000000000000..238e0f8d2d52ee7b6394a8059bdb6d4043c0df5c
--- /dev/null
+++ b/vsfilter/dsutil/NullRenderers.h
@@ -0,0 +1,93 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+
+class CNullRenderer : public CBaseRenderer
+{
+protected:
+	HRESULT DoRenderSample(IMediaSample* pSample) {return S_OK;}
+
+public:
+	CNullRenderer(REFCLSID clsid, TCHAR* pName, LPUNKNOWN pUnk, HRESULT* phr);
+};
+
+[uuid("579883A0-4E2D-481F-9436-467AAFAB7DE8")]
+class CNullVideoRenderer : public CNullRenderer
+{
+protected:
+	HRESULT CheckMediaType(const CMediaType* pmt);
+
+public:
+	CNullVideoRenderer(LPUNKNOWN pUnk, HRESULT* phr);
+};
+
+[uuid("DD9ED57D-6ABF-42E8-89A2-11D04798DC58")]
+class CNullUVideoRenderer : public CNullRenderer
+{
+protected:
+	HRESULT CheckMediaType(const CMediaType* pmt);
+
+public:
+	CNullUVideoRenderer(LPUNKNOWN pUnk, HRESULT* phr);
+};
+
+[uuid("0C38BDFD-8C17-4E00-A344-F89397D3E22A")]
+class CNullAudioRenderer : public CNullRenderer
+{
+protected:
+	HRESULT CheckMediaType(const CMediaType* pmt);
+
+public:
+	CNullAudioRenderer(LPUNKNOWN pUnk, HRESULT* phr);
+};
+
+[uuid("64A45125-7343-4772-9DA4-179FAC9D462C")]
+class CNullUAudioRenderer : public CNullRenderer
+{
+protected:
+	HRESULT CheckMediaType(const CMediaType* pmt);
+
+public:
+	CNullUAudioRenderer(LPUNKNOWN pUnk, HRESULT* phr);
+};
+
+[uuid("655D7613-C26C-4A25-BBBD-3C9C516122CC")]
+class CNullTextRenderer : public CBaseFilter, public CCritSec
+{
+	class CTextInputPin : public CBaseInputPin
+	{
+	public:
+		CTextInputPin(CBaseFilter* pFilter, CCritSec* pLock, HRESULT* phr) 
+			: CBaseInputPin(NAME("CTextInputPin"), pFilter, pLock, phr, L"In") {}
+	    HRESULT CheckMediaType(const CMediaType* pmt);
+	};
+
+	CAutoPtr<CTextInputPin> m_pInput;
+
+public:
+	CNullTextRenderer(LPUNKNOWN pUnk, HRESULT* phr);
+	int GetPinCount() {return (int)!!m_pInput;}
+	CBasePin* GetPin(int n) {return n == 0 ? (CBasePin*)m_pInput : NULL;}
+};
+
diff --git a/vsfilter/dsutil/a_yuv2rgb.asm b/vsfilter/dsutil/a_yuv2rgb.asm
new file mode 100644
index 0000000000000000000000000000000000000000..921fe7cfe306b10968ebe1128b984f09deec5da6
--- /dev/null
+++ b/vsfilter/dsutil/a_yuv2rgb.asm
@@ -0,0 +1,1910 @@
+;	VirtualDub - Video processing and capture application
+;	Copyright (C) 1998-2001 Avery Lee
+;
+;	This program is free software; you can redistribute it and/or modify
+;	it under the terms of the GNU General Public License as published by
+;	the Free Software Foundation; either version 2 of the License, or
+;	(at your option) any later version.
+;
+;	This program is distributed in the hope that it will be useful,
+;	but WITHOUT ANY WARRANTY; without even the implied warranty of
+;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;	GNU General Public License for more details.
+;
+;	You should have received a copy of the GNU General Public License
+;	along with this program; if not, write to the Free Software
+;	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	.686
+	.mmx
+	.xmm
+	.model	flat
+
+	extern _YUV_Y_table: dword
+	extern _YUV_U_table: dword
+	extern _YUV_V_table: dword
+	extern _YUV_clip_table: byte
+	extern _YUV_clip_table16: byte
+
+	.const
+
+		align	16
+
+MMX_10w		dq	00010001000100010h
+MMX_80w		dq	00080008000800080h
+MMX_00FFw	dq	000FF00FF00FF00FFh
+MMX_FF00w	dq	0FF00FF00FF00FF00h
+MMX_Ublucoeff	dq	00081008100810081h
+MMX_Vredcoeff	dq	00066006600660066h
+MMX_Ugrncoeff	dq	0FFE7FFE7FFE7FFE7h
+MMX_Vgrncoeff	dq	0FFCCFFCCFFCCFFCCh
+MMX_Ycoeff	dq	0004A004A004A004Ah
+MMX_rbmask	dq	07c1f7c1f7c1f7c1fh
+MMX_grnmask	dq	003e003e003e003e0h
+MMX_grnmask2	dq	000f800f800f800f8h
+MMX_clip	dq	07c007c007c007c00h
+
+MMX_Ucoeff0	dq	000810000FFE70081h
+MMX_Ucoeff1	dq	0FFE700810000FFE7h
+MMX_Ucoeff2	dq	00000FFE700810000h
+MMX_Vcoeff0	dq	000000066FFCC0000h
+MMX_Vcoeff1	dq	0FFCC00000066FFCCh
+MMX_Vcoeff2	dq	00066FFCC00000066h
+
+	.code
+
+	public _asm_YUVtoRGB32_row
+	public _asm_YUVtoRGB24_row
+	public _asm_YUVtoRGB16_row
+	public _asm_YUVtoRGB32_row_MMX
+	public _asm_YUVtoRGB24_row_MMX
+	public _asm_YUVtoRGB16_row_MMX
+	public _asm_YUVtoRGB32_row_ISSE
+	public _asm_YUVtoRGB24_row_ISSE
+	public _asm_YUVtoRGB16_row_ISSE
+
+;	asm_YUVtoRGB_row(
+;		Pixel *ARGB1_pointer,
+;		Pixel *ARGB2_pointer,
+;		YUVPixel *Y1_pointer,
+;		YUVPixel *Y2_pointer,
+;		YUVPixel *U_pointer,
+;		YUVPixel *V_pointer,
+;		long width
+;		);
+
+ARGB1_pointer	equ	[esp+ 4+16]
+ARGB2_pointer	equ	[esp+ 8+16]
+Y1_pointer	equ	[esp+12+16]
+Y2_pointer	equ	[esp+16+16]
+U_pointer	equ	[esp+20+16]
+V_pointer	equ	[esp+24+16]
+count		equ	[esp+28+16]
+
+_asm_YUVtoRGB32_row:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,3
+	add	eax,eax
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer			;[C]
+	mov	edi,V_pointer			;[C]
+	xor	edx,edx				;[C]
+	xor	ecx,ecx				;[C]
+	jmp	short col_loop_start
+
+col_loop:
+	mov	ch,[_YUV_clip_table+ebx-3f00h]	;[4] edx = [0][0][red][green]
+	mov	esi,U_pointer			;[C]
+	shl	ecx,8				;[4] edx = [0][red][green][0]
+	mov	edi,V_pointer			;[C]
+	mov	cl,[_YUV_clip_table+edx-3f00h]	;[4] edx = [0][r][g][b] !!
+	xor	edx,edx				;[C]
+	mov	[eax+ebp*8-4],ecx		;[4] 
+	xor	ecx,ecx				;[C]
+col_loop_start:
+	mov	cl,[esi + ebp]			;[C] eax = U
+	mov	dl,[edi + ebp]			;[C] ebx = V
+	mov	eax,Y1_pointer			;[1] 
+	xor	ebx,ebx				;[1] 
+	mov	esi,[_YUV_U_table + ecx*4]	;[C] eax = [b impact][u-g impact]
+	mov	ecx,[_YUV_V_table + edx*4]	;[C] ebx = [r impact][v-g impact]
+	mov	edi,esi				;[C]
+	mov	bl,[eax + ebp*2]		;[1] ebx = Y1 value
+	shr	esi,16				;[C] eax = blue impact
+	add	edi,ecx				;[C] edi = [junk][g impact]
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[1] ebx = Y impact
+	and	ecx,0ffff0000h			;[C]
+	mov	edx,ebx				;[1] edx = Y impact
+	add	esi,ecx				;[C] eax = [r impact][b impact]
+	and	edi,0000ffffh			;[C]
+	add	ebx,esi				;[1] ebx = [red][blue]
+	mov	ecx,ebx				;[1] edi = [red][blue]
+	and	edx,0000ffffh			;[1] ecx = green
+	shr	ebx,16				;[1] ebx = red
+	and	ecx,0000ffffh			;[1] edi = blue
+	mov	dl,[_YUV_clip_table+edx+edi-3f00h]	;[1] edx = [0][0][junk][green]
+	mov	eax,Y1_pointer			;[2] 
+	mov	dh,[_YUV_clip_table+ebx-3f00h]	;[1] edx = [0][0][red][green]
+	xor	ebx,ebx				;[2] 
+	shl	edx,8				;[1] edx = [0][red][green][0]
+	mov	bl,[eax + ebp*2 + 1]		;[2] ebx = Y1 value
+	mov	eax,ARGB1_pointer		;[1] 
+	mov	dl,[_YUV_clip_table+ecx-3f00h]	;[1] edx = [0][r][g][b] !!
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[2] ebx = Y impact
+	mov	ecx,0000ffffh			;[2] 
+
+	and	ecx,ebx				;[2]
+	add	ebx,esi				;[2] ebx = [red][blue]
+
+	mov	[eax+ebp*8],edx			;[1] 
+	mov	edx,ebx				;[2]
+
+	shr	ebx,16				;[2] ebx = red
+	mov	eax,Y2_pointer			;[3] 
+
+	and	edx,0000ffffh			;[2]
+	mov	cl,[_YUV_clip_table+ecx+edi-3f00h]	;[2] edx = [0][0][junk][green]	
+
+	mov	al,[eax + ebp*2]		;[3] ebx = Y1 value
+	mov	ch,[_YUV_clip_table+ebx-3f00h]	;[2] edx = [0][0][red][green]
+
+	shl	ecx,8				;[2] edx = [0][red][green][0]
+	and	eax,000000ffh			;[3] 
+
+	mov	cl,[_YUV_clip_table+edx-3f00h]	;[2] edx = [0][r][g][b] !!
+	mov	edx,ARGB1_pointer		;[2] 
+
+	mov	ebx,[_YUV_Y_table + eax*4]	;[3] ebx = Y impact
+	mov	eax,0000ffffh
+
+	and	eax,ebx				;[3] edi = [red][blue]
+	add	ebx,esi				;[3] ebx = [red][blue]
+
+	mov	[edx+ebp*8+4],ecx		;[2] 
+	mov	edx,ebx				;[3]
+
+	shr	ebx,16				;[3] ebx = red
+	mov	ecx,Y2_pointer			;[4] 
+
+	and	edx,0000ffffh			;[3] ecx = green
+	mov	al,[_YUV_clip_table+eax+edi-3f00h]	;[3] edx = [0][0][junk][green]
+
+	mov	cl,[ecx + ebp*2+1]		;[4] ebx = Y1 value
+	mov	ah,[_YUV_clip_table+ebx-3f00h]	;[3] edx = [0][0][red][green]
+
+	shl	eax,8				;[3] edx = [0][red][green][0]
+	and	ecx,000000ffh			;[4] 
+
+	mov	al,[_YUV_clip_table+edx-3f00h]	;[3] edx = [0][r][g][b] !!
+	mov	edx,ARGB2_pointer		;[3] 
+
+	mov	ebx,[_YUV_Y_table + ecx*4]	;[4] ebx = Y impact
+	mov	ecx,0000ffffh			;[4]
+
+	and	ecx,ebx				;[4] ecx = [0][Y-impact]
+	add	ebx,esi				;[4] ebx = [red][blue]
+
+	mov	[edx+ebp*8],eax			;[3] 
+	mov	edx,ebx				;[4] edx = [red][blue]
+
+	shr	ebx,16				;[4] ebx = red
+	mov	cl,[_YUV_clip_table+ecx+edi-3f00h]	;[4] edx = [0][0][junk][green]
+
+	and	edx,0000ffffh			;[4] edx = blue
+	mov	eax,ARGB2_pointer		;[4] 
+
+	inc	ebp
+
+	jnz	col_loop
+
+	mov	ch,[_YUV_clip_table+ebx-3f00h]	;[4] edx = [0][0][red][green]
+	shl	ecx,8				;[4] edx = [0][red][green][0]
+	mov	cl,[_YUV_clip_table+edx-3f00h]	;[4] edx = [0][r][g][b] !!
+	mov	[eax+ebp*8-4],ecx		;[4] 
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+;MMX_test	dq	7060504030201000h
+
+_asm_YUVtoRGB32_row_MMX:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,3
+	add	eax,eax
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+col_loop_MMX:
+	movd	mm0, dword ptr [esi+ebp]		;U (byte)
+	pxor	mm7,mm7
+
+	movd	mm1, dword ptr [edi+ebp]		;V (byte)
+	punpcklbw mm0,mm7		;U (word)
+
+	psubw	mm0,MMX_80w
+	punpcklbw mm1,mm7		;V (word)
+
+	psubw	mm1,MMX_80w
+	movq	mm2,mm0
+
+	pmullw	mm2,MMX_Ugrncoeff
+	movq	mm3,mm1
+
+	pmullw	mm3,MMX_Vgrncoeff
+	pmullw	mm0,MMX_Ublucoeff
+	pmullw	mm1,MMX_Vredcoeff
+	paddw	mm2,mm3
+
+	;mm0: blue
+	;mm1: red
+	;mm2: green
+
+	movq	mm6,[ecx+ebp*2]		;Y
+	pand	mm6,MMX_00FFw
+	psubw	mm6,MMX_10w
+	pmullw	mm6,MMX_Ycoeff
+	movq	mm4,mm6
+	paddw	mm6,mm0			;mm6: <B3><B2><B1><B0>
+	movq	mm5,mm4
+	paddw	mm4,mm1			;mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;mm5: <G3><G2><G1><G0>
+	psraw	mm6,6
+	psraw	mm4,6
+	packuswb mm6,mm6		;mm6: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm4,mm4		;mm4: R3R2R1R0R3R2R1R0
+	punpcklbw mm6,mm4		;mm6: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm4,mm6
+	punpcklbw mm6,mm5		;mm6: G1R1G1B2G0R0G0B0
+	punpckhbw mm4,mm5		;mm4: G3R3G3B3G2R2G2B2
+
+	movq	mm7,[ecx+ebp*2]		;Y
+	psrlw	mm7,8
+	psubw	mm7,MMX_10w
+	pmullw	mm7,MMX_Ycoeff
+	movq	mm3,mm7
+	paddw	mm7,mm0			;mm7: final blue
+	movq	mm5,mm3
+	paddw	mm3,mm1			;mm3: final red
+	paddw	mm5,mm2			;mm5: final green
+	psraw	mm7,6
+	psraw	mm3,6
+	packuswb mm7,mm7		;mm7: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm3,mm3		;mm3: R3R2R1R0R3R2R1R0
+	punpcklbw mm7,mm3		;mm7: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm3,mm7
+	punpcklbw mm7,mm5		;mm7: G1R1G1B2G0R0G0B0
+	punpckhbw mm3,mm5		;mm3: G3R3G3B3G2R2G2B2
+
+	;mm3	P7:P5
+	;mm4	P6:P4
+	;mm6	P2:P0
+	;mm7	P3:P1
+
+	movq	mm5,mm6
+	punpckldq mm5,mm7		;P1:P0
+	punpckhdq mm6,mm7		;P3:P2
+	movq	mm7,mm4
+	punpckldq mm4,mm3		;P5:P4
+	punpckhdq mm7,mm3		;P7:P6
+
+	movq	[eax+ebp*8],mm5
+	movq	[eax+ebp*8+8],mm6
+	movq	[eax+ebp*8+16],mm4
+	movq	[eax+ebp*8+24],mm7
+
+	movq	mm6,[edx+ebp*2]		;Y
+	pand	mm6,MMX_00FFw
+	psubw	mm6,MMX_10w
+	pmullw	mm6,MMX_Ycoeff
+	movq	mm4,mm6
+	paddw	mm6,mm0			;mm6: <B3><B2><B1><B0>
+	movq	mm5,mm4
+	paddw	mm4,mm1			;mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;mm5: <G3><G2><G1><G0>
+	psraw	mm6,6
+	psraw	mm4,6
+	packuswb mm6,mm6		;mm6: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm4,mm4		;mm4: R3R2R1R0R3R2R1R0
+	punpcklbw mm6,mm4		;mm6: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm4,mm6
+	punpcklbw mm6,mm5		;mm6: G1R1G1B2G0R0G0B0
+	punpckhbw mm4,mm5		;mm4: G3R3G3B3G2R2G2B2
+
+	movq	mm7,[edx+ebp*2]		;Y
+	psrlw	mm7,8
+	psubw	mm7,MMX_10w
+	pmullw	mm7,MMX_Ycoeff
+	movq	mm3,mm7
+	paddw	mm7,mm0			;mm7: final blue
+	movq	mm5,mm3
+	paddw	mm3,mm1			;mm3: final red
+	paddw	mm5,mm2			;mm5: final green
+	psraw	mm7,6
+	psraw	mm3,6
+	packuswb mm7,mm7		;mm7: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm3,mm3		;mm3: R3R2R1R0R3R2R1R0
+	punpcklbw mm7,mm3		;mm7: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm3,mm7
+	punpcklbw mm7,mm5		;mm7: G1R1G1B2G0R0G0B0
+	punpckhbw mm3,mm5		;mm3: G3R3G3B3G2R2G2B2
+
+	;mm3	P7:P5
+	;mm4	P6:P4
+	;mm6	P2:P0
+	;mm7	P3:P1
+
+	movq	mm5,mm6
+	punpckldq mm5,mm7		;P1:P0
+	punpckhdq mm6,mm7		;P3:P2
+	movq	mm7,mm4
+	punpckldq mm4,mm3		;P5:P4
+	punpckhdq mm7,mm3		;P7:P6
+
+	movq	[ebx+ebp*8   ],mm5
+	movq	[ebx+ebp*8+ 8],mm6
+
+	movq	[ebx+ebp*8+16],mm4
+	movq	[ebx+ebp*8+24],mm7
+
+	add	ebp,4
+
+	jnz	col_loop_MMX
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+;**************************************************************************
+;
+;	asm_YUVtoRGB24_row(
+;		Pixel *ARGB1_pointer,
+;		Pixel *ARGB2_pointer,
+;		YUVPixel *Y1_pointer,
+;		YUVPixel *Y2_pointer,
+;		YUVPixel *U_pointer,
+;		YUVPixel *V_pointer,
+;		long width
+;		);
+
+ARGB1_pointer	equ	[esp+ 4+16]
+ARGB2_pointer	equ	[esp+ 8+16]
+Y1_pointer	equ	[esp+12+16]
+Y2_pointer	equ	[esp+16+16]
+U_pointer	equ	[esp+20+16]
+V_pointer	equ	[esp+24+16]
+count		equ	[esp+28+16]
+
+_asm_YUVtoRGB24_row:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	add	eax,eax
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer			;[C]
+	mov	edi,V_pointer			;[C]
+	xor	edx,edx				;[C]
+	xor	ecx,ecx				;[C]
+
+col_loop24:
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	xor	eax,eax
+	xor	ebx,ebx
+	mov	al,[esi + ebp]			;eax = U
+	mov	bl,[edi + ebp]			;ebx = V
+	mov	eax,[_YUV_U_table + eax*4]	;eax = [b impact][u-g impact]
+	mov	edi,[_YUV_V_table + ebx*4]	;edi = [r impact][v-g impact]
+
+	mov	ecx,eax				;[C]
+	mov	esi,Y1_pointer			;[1]
+
+	mov	edx,edi				;[C]
+	xor	ebx,ebx				;[1]
+
+	shr	eax,16				;[C] eax = blue impact
+	mov	bl,[esi + ebp*2]		;[1] ebx = Y1 value
+
+	and	edi,0ffff0000h			;[C] edi = [r impact][0]
+	add	ecx,edx				;[C] ecx = [junk][g impact]
+
+	add	eax,edi				;[C] eax = [r impact][b impact]
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[1] ebx = Y impact
+
+	;eax = [r][b]
+	;ecx = [g]
+
+	mov	esi,ebx				;[1]
+	add	ebx,eax				;[1] ebx = [red][blue]
+
+	add	esi,ecx				;[1] edx = [junk][green]
+	mov	edi,ebx				;[1] edi = [red][blue]
+
+	shr	ebx,16				;[1] ebx = red
+	and	esi,0000ffffh			;[1] ecx = green
+
+	and	edi,0000ffffh			;edi = blue
+	xor	edx,edx
+
+	mov	bh,[_YUV_clip_table+ebx-3f00h]	;bh = red
+	mov	dl,[_YUV_clip_table+esi-3f00h]	;dl = green
+
+	mov	esi,Y1_pointer			;[2]
+	mov	bl,[_YUV_clip_table+edi-3f00h]	;bl = blue
+
+	mov	edi,ARGB1_pointer		;[1]
+	mov	[edi+2],bh			;[1]
+
+	mov	[edi+0],bl			;[1]
+	xor	ebx,ebx				;[2]
+
+	mov	[edi+1],dl			;[1]
+
+	mov	bl,[esi + ebp*2 + 1]		;[2] ebx = Y1 value
+	mov	esi,ecx				;[2]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[2] ebx = Y impact
+	mov	edi,0000ffffh			;[2]
+
+	add	esi,ebx				;[2] edx = [junk][green]
+	add	ebx,eax				;[2] ebx = [red][blue]
+
+	and	edi,ebx				;[2] edi = blue
+	and	esi,0000ffffh			;[2] ecx = green
+
+	shr	ebx,16				;ebx = red
+	xor	edx,edx
+
+	mov	bh,[_YUV_clip_table+ebx-3f00h]	;bh = red
+	mov	dl,[_YUV_clip_table+esi-3f00h]	;dl = green
+
+	mov	esi,Y2_pointer			;[3]
+	mov	bl,[_YUV_clip_table+edi-3f00h]	;bl = blue
+
+	mov	edi,ARGB1_pointer		;[2]
+	mov	[edi+5],bh			;[2]
+
+	mov	[edi+4],dl			;[2]
+	mov	[edi+3],bl			;[2]
+
+	xor	ebx,ebx				;[3]
+
+	mov	bl,[esi + ebp*2]		;[3] ebx = Y1 value
+	mov	edi,ecx				;[2]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[3] ebx = Y impact
+	mov	esi,0000ffffh			;[3]
+
+	add	edi,ebx				;[3] edx = [junk][green]
+	add	ebx,eax				;[3] ebx = [red][blue]
+
+	and	esi,ebx				;[3] edi = blue
+	and	edi,0000ffffh			;ecx = green
+
+	shr	ebx,16				;ebx = red
+	xor	edx,edx
+
+	mov	dl,[_YUV_clip_table+edi-3f00h]	;dl = green
+	mov	edi,ARGB2_pointer		;[3]
+
+	mov	bh,[_YUV_clip_table+ebx-3f00h]	;bh = red
+	mov	bl,[_YUV_clip_table+esi-3f00h]	;bl = blue
+
+	mov	esi,Y2_pointer			;[4]
+	mov	[edi+2],bh
+
+	mov	[edi+0],bl
+	xor	ebx,ebx				;[4]
+
+	mov	[edi+1],dl
+	mov	bl,[esi + ebp*2 + 1]		;[4] ebx = Y1 value
+
+	mov	edi,0000ffffh			;[4]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[4] ebx = Y impact
+	xor	edx,edx
+
+	add	ecx,ebx				;[4] ecx = [junk][green]
+	add	ebx,eax				;ebx = [red][blue]
+
+	and	edi,ebx				;edi = blue
+	and	ecx,0000ffffh			;ecx = green
+
+	shr	ebx,16				;ebx = red
+	mov	esi,ARGB2_pointer
+
+	mov	bl,[_YUV_clip_table+ebx-3f00h]	;bh = red
+	mov	dl,[_YUV_clip_table+ecx-3f00h]	;dl = green
+
+	mov	al,[_YUV_clip_table+edi-3f00h]	;bl = blue
+	mov	[esi+5],bl
+
+	mov	[esi+4],dl
+	mov	ecx,ARGB1_pointer
+
+	mov	[esi+3],al
+	add	esi,6
+
+	mov	ARGB2_pointer,esi
+	add	ecx,6
+
+	mov	ARGB1_pointer,ecx
+
+	inc	ebp
+	jnz	col_loop24
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+_asm_YUVtoRGB24_row_MMX:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	add	eax,eax
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+col_loop_MMX24:
+	movd		mm0, dword ptr [esi+ebp]	;U (byte)
+	pxor		mm7,mm7
+
+	movd		mm1, dword ptr [edi+ebp]	;V (byte)
+	punpcklbw mm0,mm7		;U (word)
+
+	movd		mm2, dword ptr [ecx+ebp*2]	;Y low
+	punpcklbw mm1,mm7		;V (word)
+
+	movd		mm3, dword ptr [edx+ebp*2]	;Y high
+	punpcklbw mm2,mm7		;Y1 (word)
+
+	psubw		mm2,MMX_10w
+	punpcklbw mm3,mm7		;Y2 (word)
+
+	psubw		mm3,MMX_10w
+
+	psubw		mm0,MMX_80w
+	psubw		mm1,MMX_80w
+
+	;group 1
+
+	pmullw		mm2,MMX_Ycoeff	;[lazy]
+	movq		mm6,mm0
+	pmullw		mm3,MMX_Ycoeff	;[lazy]
+	movq		mm7,mm1
+	punpcklwd	mm6,mm6		;mm6 = U1U1U0U0
+	movq		mm4,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpckldq	mm6,mm6		;mm6 = U0U0U0U0
+	movq		mm5,mm3		;mm3 = Y3Y2Y1Y0		[low]
+	punpcklwd	mm7,mm7		;mm7 = V1V1V0V0
+	punpckldq	mm7,mm7		;mm7 = V0V0V0V0
+
+	pmullw		mm6,MMX_Ucoeff0
+	punpcklwd	mm4,mm4		;mm4 = Y1Y1Y0Y0		[high]
+	pmullw		mm7,MMX_Vcoeff0
+	punpcklwd	mm5,mm5		;mm5 = Y1Y1Y0Y0		[low]
+
+	punpcklwd	mm4,mm2		;mm4 = Y1Y0Y0Y0
+	punpcklwd	mm5,mm3		;mm5 = Y1Y0Y0Y0
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	packuswb	mm4,mm4
+	packuswb	mm5,mm5
+
+	;group 2
+
+	movd		dword ptr [eax+0],mm4	;[lazy write]
+	movq		mm4,mm0
+	movd		dword ptr [ebx+0],mm5	;[lazy write]
+	movq		mm5,mm1
+
+	punpcklwd	mm4,mm4		;mm6 = U1U1U0U0
+	movq		mm6,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpcklwd	mm5,mm5		;mm6 = V1V1V0V0
+	movq		mm7,mm3		;mm3 = Y3Y2Y1Y0		[low]
+
+	pmullw		mm4,MMX_Ucoeff1
+	psrlq		mm6,16		;mm4 = 00Y3Y2Y1		[high]
+	pmullw		mm5,MMX_Vcoeff1
+	psrlq		mm7,16		;mm4 = 00Y3Y2Y1		[low]
+
+	punpcklwd	mm6,mm6		;mm4 = Y2Y2Y1Y1		[high]
+	punpcklwd	mm7,mm7		;mm5 = Y2Y2Y1Y1		[high]
+
+	paddw		mm6,mm4
+	paddw		mm7,mm4
+	paddw		mm6,mm5
+	paddw		mm7,mm5
+
+	psraw		mm6,6
+	psraw		mm7,6
+
+	packuswb	mm6,mm6
+	packuswb	mm7,mm7
+
+	;group 3
+
+	movd		dword ptr [eax+4],mm6	;[lazy write]
+	movq		mm6,mm0
+	movd		dword ptr [ebx+4],mm7	;[lazy write]
+	movq		mm7,mm1
+
+	movq		mm4,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpcklwd	mm6,mm6		;mm6 = U1U1U0U0
+	movq		mm5,mm3		;mm3 = Y3Y2Y1Y0		[low]
+	punpckhdq	mm6,mm6		;mm6 = U1U1U1U1
+	punpcklwd	mm7,mm7		;mm7 = V1V1V0V0
+	punpckhdq	mm7,mm7		;mm7 = V1V1V1V1
+
+	pmullw		mm6,MMX_Ucoeff2
+	punpckhwd	mm2,mm2		;mm2 = Y3Y3Y2Y2		[high]
+	pmullw		mm7,MMX_Vcoeff2
+	punpckhwd	mm3,mm3		;mm3 = Y3Y3Y2Y2		[low]
+
+	punpckhdq	mm4,mm2		;mm4 = Y3Y3Y3Y2		[high]
+	punpckhdq	mm5,mm3		;mm5 = Y3Y3Y3Y2		[low]
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	;next 3 groups
+
+	movd		mm2, dword ptr [ecx+ebp*2+4]	;Y low
+	packuswb	mm4,mm4		;[lazy]
+
+	movd		mm3, dword ptr [edx+ebp*2+4]	;Y high
+	packuswb	mm5,mm5		;[lazy]
+
+	movd		dword ptr [eax+8],mm4	;[lazy write]
+	pxor		mm7,mm7
+
+	movd		dword ptr [ebx+8],mm5	;[lazy write]
+	punpcklbw	mm2,mm7		;U (word)
+
+
+	psubw		mm2,MMX_10w
+	punpcklbw	mm3,mm7		;V (word)
+
+	psubw		mm3,MMX_10w
+
+
+	;group 1
+
+	pmullw		mm2,MMX_Ycoeff	;[init]
+	movq		mm6,mm0
+
+	pmullw		mm3,MMX_Ycoeff	;[init]
+	punpckhwd	mm6,mm6		;mm6 = U3U3U2U2
+
+	movq		mm7,mm1
+	punpckldq	mm6,mm6		;mm6 = U2U2U2U2
+	movq		mm4,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpckhwd	mm7,mm7		;mm7 = V3V3V2V2
+	movq		mm5,mm3		;mm3 = Y3Y2Y1Y0		[low]
+	punpckldq	mm7,mm7		;mm7 = V2V2V2V2
+
+	pmullw		mm6,MMX_Ucoeff0
+	punpcklwd	mm4,mm4		;mm4 = Y1Y1Y0Y0		[high]
+	pmullw		mm7,MMX_Vcoeff0
+	punpcklwd	mm5,mm5		;mm5 = Y1Y1Y0Y0		[low]
+
+	punpcklwd	mm4,mm2		;mm4 = Y1Y0Y0Y0
+	punpcklwd	mm5,mm3		;mm5 = Y1Y0Y0Y0
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	packuswb	mm4,mm4
+	packuswb	mm5,mm5
+
+	;group 2
+
+	movd		dword ptr [eax+12],mm4
+	movq		mm6,mm0
+	movd		dword ptr [ebx+12],mm5
+	movq		mm7,mm1
+
+	punpckhwd	mm6,mm6		;mm6 = U3U3U2U2
+	movq		mm4,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpckhwd	mm7,mm7		;mm6 = V3V3V2V2
+	movq		mm5,mm3		;mm3 = Y3Y2Y1Y0		[low]
+
+	pmullw		mm6,MMX_Ucoeff1
+	psrlq		mm4,16		;mm4 = 00Y3Y2Y1		[high]
+	pmullw		mm7,MMX_Vcoeff1
+	psrlq		mm5,16		;mm4 = 00Y3Y2Y1		[low]
+
+	punpcklwd	mm4,mm4		;mm4 = Y2Y2Y1Y1		[high]
+	punpcklwd	mm5,mm5		;mm5 = Y2Y2Y1Y1		[high]
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	packuswb	mm4,mm4
+	packuswb	mm5,mm5
+
+	;group 3
+
+	movq		mm6,mm2		;mm4 = Y3Y2Y1Y0		[high]
+	punpckhwd	mm0,mm0		;mm6 = U3U3U2U2
+
+	movq		mm7,mm3		;mm3 = Y3Y2Y1Y0		[low]
+	punpckhdq	mm0,mm0		;mm6 = U3U3U3U3
+
+	movd		dword ptr [eax+16],mm4	;[lazy write]
+	punpckhwd	mm1,mm1		;mm7 = V3V3V2V2
+
+	movd		dword ptr [ebx+16],mm5	;[lazy write]
+	punpckhdq	mm1,mm1		;mm7 = V3V3V3V3
+
+	pmullw		mm0,MMX_Ucoeff2
+	punpckhwd	mm2,mm2		;mm2 = Y3Y3Y2Y2		[high]
+	pmullw		mm1,MMX_Vcoeff2
+	punpckhwd	mm3,mm3		;mm3 = Y3Y3Y2Y2		[low]
+
+	punpckhdq	mm6,mm2		;mm4 = Y3Y3Y3Y2		[high]
+	punpckhdq	mm7,mm3		;mm5 = Y3Y3Y3Y2		[low]
+
+	paddw		mm6,mm0
+	paddw		mm7,mm0
+	paddw		mm6,mm1
+	paddw		mm7,mm1
+
+	psraw		mm6,6
+	psraw		mm7,6
+
+	packuswb	mm6,mm6
+	packuswb	mm7,mm7
+
+	movd		dword ptr [eax+20],mm6
+	add	eax,24
+	movd		dword ptr [ebx+20],mm7
+	add	ebx,24
+
+	;done
+
+	add	ebp,4
+	jnz	col_loop_MMX24
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+;**************************************************************************
+
+_asm_YUVtoRGB16_row:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,2
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	eax,eax
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer			;[C]
+	mov	edi,V_pointer			;[C]
+	xor	edx,edx				;[C]
+	xor	ecx,ecx				;[C]
+
+col_loop16:
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	xor	eax,eax
+	xor	ebx,ebx
+	mov	al,[esi + ebp]			;eax = U
+	mov	bl,[edi + ebp]			;ebx = V
+	mov	eax,[_YUV_U_table + eax*4]	;eax = [b impact][u-g impact]
+	mov	edi,[_YUV_V_table + ebx*4]	;edi = [r impact][v-g impact]
+
+	mov	ecx,eax				;[C]
+	mov	esi,Y1_pointer			;[1]
+
+	mov	edx,edi				;[C]
+	xor	ebx,ebx				;[1]
+
+	shr	eax,16				;[C] eax = blue impact
+	mov	bl,[esi + ebp*2]		;[1] ebx = Y1 value
+
+	and	edi,0ffff0000h			;[C] edi = [r impact][0]
+	add	ecx,edx				;[C] ecx = [junk][g impact]
+
+	add	eax,edi				;[C] eax = [r impact][b impact]
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[1] ebx = Y impact
+
+	;eax = [r][b]
+	;ecx = [g]
+
+	mov	esi,ebx				;[1]
+	add	ebx,eax				;[1] ebx = [red][blue]
+
+	add	esi,ecx				;[1] edx = [junk][green]
+	mov	edi,ebx				;[1] edi = [red][blue]
+
+	shr	ebx,16				;[1] ebx = red
+	and	esi,0000ffffh			;[1] ecx = green
+
+	and	edi,0000ffffh			;edi = blue
+	xor	edx,edx
+
+	mov	bh,[_YUV_clip_table16+ebx-3f00h]	;bh = red
+	mov	dl,[_YUV_clip_table16+esi-3f00h]	;dl = green
+
+	mov	bl,[_YUV_clip_table16+edi-3f00h]	;bl = blue
+	xor	dh,dh				;[1]
+
+;565fix	shl	bh,2				;[1]
+	shl	bh,3				;[1]
+	mov	edi,ARGB1_pointer		;[1]
+
+;565fix	shl	edx,5				;[1]
+	shl	edx,6				;[1]
+	mov	esi,Y1_pointer			;[2]
+
+	add	edx,ebx				;[1]
+	xor	ebx,ebx				;[2]
+
+	mov	[edi+ebp*4+0],dl		;[1]
+	mov	bl,[esi + ebp*2 + 1]		;[2] ebx = Y1 value
+
+	mov	[edi+ebp*4+1],dh		;[1]
+	mov	esi,ecx				;[2]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[2] ebx = Y impact
+	mov	edi,0000ffffh			;[2]
+
+	add	esi,ebx				;[2] edx = [junk][green]
+	add	ebx,eax				;[2] ebx = [red][blue]
+
+	and	edi,ebx				;[2] edi = blue
+	and	esi,0000ffffh			;[2] ecx = green
+
+	shr	ebx,16				;ebx = red
+	xor	edx,edx
+
+	mov	bh,[_YUV_clip_table16+ebx-3f00h]	;bh = red
+
+	mov	dl,[_YUV_clip_table16+esi-3f00h]	;dl = green
+	mov	bl,[_YUV_clip_table16+edi-3f00h]	;bl = blue
+
+;565fix	shl	edx,5				;[2]
+	shl	edx,6				;[2]
+	mov	edi,ARGB1_pointer		;[2]
+
+;565fix	shl	bh,2				;[2]
+	shl	bh,3				;[2]
+	mov	esi,Y2_pointer			;[3]
+
+	add	edx,ebx				;[2]
+	xor	ebx,ebx				;[3]
+
+	mov	[edi+ebp*4+2],dl		;[2]
+	mov	bl,[esi + ebp*2]		;[3] ebx = Y1 value
+
+	mov	[edi+ebp*4+3],dh		;[2]
+	mov	edi,ecx				;[2]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[3] ebx = Y impact
+	mov	esi,0000ffffh			;[3]
+
+	add	edi,ebx				;[3] edx = [junk][green]
+	add	ebx,eax				;[3] ebx = [red][blue]
+
+	and	esi,ebx				;[3] edi = blue
+	and	edi,0000ffffh			;ecx = green
+
+	shr	ebx,16				;ebx = red
+	xor	edx,edx
+
+	mov	dl,[_YUV_clip_table16+edi-3f00h]	;dl = green
+	mov	edi,ARGB2_pointer		;[3]
+
+;565fix	shl	edx,5
+	shl	edx,6
+	mov	bh,[_YUV_clip_table16+ebx-3f00h]	;bh = red
+
+	mov	bl,[_YUV_clip_table16+esi-3f00h]	;bl = blue
+	mov	esi,Y2_pointer			;[4]
+
+;565fix	shl	bh,2				;[3]
+	shl	bh,3				;[3]
+	nop
+
+	add	edx,ebx				;[3]
+	xor	ebx,ebx				;[4]
+
+	mov	[edi+ebp*4+0],dl		;[3]
+	mov	bl,[esi + ebp*2 + 1]		;[4] ebx = Y1 value
+
+	mov	[edi+ebp*4+1],dh		;[3]
+	mov	edi,0000ffffh			;[4]
+
+	mov	ebx,[_YUV_Y_table + ebx*4]	;[4] ebx = Y impact
+	xor	edx,edx
+
+	add	ecx,ebx				;[4] ecx = [junk][green]
+	add	ebx,eax				;ebx = [red][blue]
+
+	and	edi,ebx				;edi = blue
+	and	ecx,0000ffffh			;ecx = green
+
+	shr	ebx,16				;ebx = red
+	mov	esi,ARGB2_pointer
+
+	mov	dl,[_YUV_clip_table16+ecx-3f00h]	;dl = green
+	mov	al,[_YUV_clip_table16+edi-3f00h]	;bl = blue
+
+;565fix	shl	edx,5
+	shl	edx,6
+	mov	ah,[_YUV_clip_table16+ebx-3f00h]	;bh = red
+
+;565fix	shl	ah,2
+	shl	ah,3
+
+	add	eax,edx
+
+	mov	[esi+ebp*4+2],al
+	mov	[esi+ebp*4+3],ah
+
+	inc	ebp
+	jnz	col_loop16
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+
+
+_asm_YUVtoRGB16_row_MMX:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,2
+	add	eax,eax
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+col_loop_MMX16:
+	movd	mm0, dword ptr [esi+ebp]		;[0       ] U (byte)
+	pxor	mm7,mm7			;[0      7] 
+
+	movd	mm1, dword ptr [edi+ebp]		;[01     7] V (byte)
+	punpcklbw mm0,mm7		;[01     7] U (word)
+
+	psubw	mm0,MMX_80w		;[01     7] 
+	punpcklbw mm1,mm7		;[01     7] V (word)
+
+	psubw	mm1,MMX_80w		;[01      ] 
+	movq	mm2,mm0			;[012     ] 
+
+	pmullw	mm2,MMX_Ugrncoeff	;[012     ] 
+	movq	mm3,mm1			;[0123    ] 
+
+	;mm0: blue
+	;mm1: red
+	;mm2: green
+
+	movq	mm6,[ecx+ebp*2]		;[0123  6 ] [1] Y
+	;<-->
+
+	pmullw	mm3,MMX_Vgrncoeff	;[0123    ] 
+	movq	mm7,mm6			;[012   67] [2] Y
+
+	pmullw	mm0,MMX_Ublucoeff	;[0123    ] 
+	psrlw	mm7,8			;[012   67] [2]
+
+	pmullw	mm1,MMX_Vredcoeff	;[0123    ] 
+	;<-->
+
+	pand	mm6,MMX_00FFw		;[012   67] [1]
+	paddw	mm2,mm3			;[012   6 ] [C]
+
+	psubw	mm6,MMX_10w		;[012   67] [1]
+
+	pmullw	mm6,MMX_Ycoeff		;[012   67] [1]
+
+	psubw	mm7,MMX_10w		;[012   67] [2]
+	movq	mm4,mm6			;[012 4 67] [1]
+
+	pmullw	mm7,MMX_Ycoeff		;[012   67] [2]
+	movq	mm5,mm6			;[012 4567] [1]
+
+	paddw	mm6,mm0			;[012 4 67] [1] mm6: <B3><B2><B1><B0>
+	paddw	mm4,mm1			;[012 4567] [1] mm4: <R3><R2><R1><R0>
+
+	paddw	mm5,mm2			;[012 4567] [1] mm5: <G3><G2><G1><G0>
+	psraw	mm4,6			;[012 4567] [1]
+
+	movq	mm3,mm7			;[01234567] [2]
+	psraw	mm5,4			;[01234567] [1]
+
+	paddw	mm7,mm0			;[01234567] [2] mm6: <B3><B2><B1><B0>
+	psraw	mm6,6			;[01234567] [1]
+
+	paddsw	mm5,MMX_clip
+	packuswb mm6,mm6		;[01234567] [1] mm6: B3B2B1B0B3B2B1B0
+
+	psubusw	mm5,MMX_clip
+	packuswb mm4,mm4		;[01234567] [1] mm4: R3R2R1R0R3R2R1R0
+
+	pand	mm5,MMX_grnmask		;[01234567] [1] mm7: <G3><G2><G1><G0>
+	psrlq	mm6,2			;[01234567] [1]
+
+	punpcklbw mm6,mm4		;[0123 567] [1] mm4: R3B3R2B2R1B1R0B0
+
+	movq	mm4,[edx+ebp*2]		;[01234567] [3] Y
+	psrlw	mm6,1			;[01234567] [1]
+
+	pand	mm6,MMX_rbmask		;[01234567] [1] mm6: <RB3><RB2><RB1><RB0>
+
+	por	mm6,mm5			;[01234 67] [1] mm6: P6P4P2P0
+	movq	mm5,mm3			;[01234567] [2]
+
+	paddw	mm3,mm1			;[01234567] [2] mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;[01234567] [2] mm5: <G3><G2><G1><G0>
+
+	pand	mm4,MMX_00FFw		;[01234567] [3]
+	psraw	mm3,6			;[01234567] [2]	
+
+	psubw	mm4,MMX_10w		;[01234567] [3]
+	psraw	mm5,4			;[01234567] [2]
+
+	pmullw	mm4,MMX_Ycoeff		;[01234567] [3]
+	psraw	mm7,6			;[01234567] [2]
+
+	paddsw	mm5,MMX_clip
+	packuswb mm3,mm3		;[01234567] [2] mm4: R3R2R1R0R3R2R1R0
+
+	psubusw	mm5,MMX_clip
+	packuswb mm7,mm7		;[01234567] [2] mm6: B3B2B1B0B3B2B1B0
+
+	pand	mm5,MMX_grnmask		;[012 4567] [2] mm7: <G3><G2><G1><G0>
+	psrlq	mm7,2			;[01234567] [2]
+
+	punpcklbw mm7,mm3		;[012 4567] [2] mm6: R3B3R2B2R1B1R0B0
+
+	movq	mm3,[edx+ebp*2]		;[01234567] [4] Y
+	psrlw	mm7,1			;[01234567] [2]
+
+	pand	mm7,MMX_rbmask		;[01234567] [2] mm6: <RB3><RB2><RB1><RB0>
+	psrlw	mm3,8			;[01234567] [4]
+
+	por	mm7,mm5			;[01234567] [2] mm7: P7P5P3P1
+	movq	mm5,mm6			;[01234567] [A]
+
+	psubw	mm3,MMX_10w		;[01234567] [4]
+	punpcklwd mm6,mm7		;[01234567] [A] mm4: P3P2P1P0
+
+	pmullw	mm3,MMX_Ycoeff		;[0123456 ] [4]
+	punpckhwd mm5,mm7		;[0123456 ] [A} mm5: P7P6P5P4
+
+	movq	[eax+ebp*4   ],mm6	;[012345  ] [A]
+	movq	mm6,mm4			;[0123456 ] [3]
+
+	movq	[eax+ebp*4+ 8],mm5	;[0123456 ] [A]
+	paddw	mm6,mm0			;[01234 6 ] [3] mm6: <B3><B2><B1><B0>
+
+	movq	mm5,mm4			;[0123456 ] [3]
+	paddw	mm4,mm1			;[0123456 ] [3] mm4: <R3><R2><R1><R0>
+
+	paddw	mm5,mm2			;[0123456 ] [3] mm5: <G3><G2><G1><G0>
+	psraw	mm4,6			;[0123456 ] [3]
+
+	movq	mm7,mm3			;[01234567] [4]
+	psraw	mm5,4			;[01234567] [3]
+
+	paddw	mm7,mm0			;[01234567] [4] mm6: <B3><B2><B1><B0>
+	psraw	mm6,6			;[01234567] [3]
+
+	movq	mm0,mm3			;[01234567] [4]
+	packuswb mm4,mm4		;[01234567] [3] mm4: R3R2R1R0R3R2R1R0
+
+
+	packuswb mm6,mm6		;[01 34567] [3] mm6: B3B2B1B0B3B2B1B0
+	paddw	mm3,mm1			;[01234567] [4] mm4: <R3><R2><R1><R0>
+
+	psrlq	mm6,2
+	paddw	mm0,mm2			;[01 34567] [4] mm5: <G3><G2><G1><G0>
+
+	paddsw	mm5,MMX_clip
+	punpcklbw mm6,mm4		;[01 3 567] [3] mm6: B3B3B2B2B1B1B0B0
+
+	psubusw	mm5,MMX_clip
+	psrlw	mm6,1			;[01 3 567] [3]
+
+	pand	mm6,MMX_rbmask		;[01 3 567] [3] mm6: <B3><B2><B1><B0>
+	psraw	mm3,6			;[01 3 567] [4]
+
+	pand	mm5,MMX_grnmask		;[01 3 567] [3] mm7: <G3><G2><G1><G0>
+	psraw	mm0,4			;[01 3 567] [4]
+
+	por	mm6,mm5			;[01 3  67] [3] mm4: P6P4P2P0	
+	psraw	mm7,6			;[01 3  67] [4]
+
+	paddsw	mm0,MMX_clip
+	packuswb mm3,mm3		;[01 3  67] [4] mm4: R3R2R1R0R3R2R1R0
+
+	psubusw	mm0,MMX_clip
+	packuswb mm7,mm7		;[01 3  67] mm6: B3B2B1B0B3B2B1B0
+
+	pand	mm0,MMX_grnmask		;[01    67] mm7: <G3><G2><G1><G0>
+	psrlq	mm7,2
+
+	punpcklbw mm7,mm3		;[01    67] mm6: R3B3R2B2R1B1R0B0
+	movq	mm1,mm6
+
+	psrlw	mm7,1
+	add	ebp,4
+
+	pand	mm7,MMX_rbmask		;[01    67] mm6: <B3><B2><B1><B0>
+
+	por	mm0,mm7			;[01    67] mm0: P7P5P3P1
+
+	punpcklwd mm6,mm0		;[01    6 ] mm4: P3P2P1P0
+
+	punpckhwd mm1,mm0		;[ 1    6 ] mm5: P7P6P5P4
+	movq	[ebx+ebp*4-16],mm6
+
+	movq	[ebx+ebp*4- 8],mm1
+	jnz	col_loop_MMX16
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+;--------------------------------------------------------------------------
+
+_asm_YUVtoRGB32_row_ISSE:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,3
+	add	eax,eax
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+col_loop_SSE:
+	prefetchnta [esi+ebp+32]
+	prefetchnta [edi+ebp+32]
+	prefetchnta [ecx+ebp*2+32]
+	prefetchnta [edx+ebp*2+32]
+
+	movd	mm0, dword ptr [esi+ebp]		;U (byte)
+	pxor	mm7,mm7
+
+	movd	mm1, dword ptr [edi+ebp]		;V (byte)
+	punpcklbw mm0,mm7		;U (word)
+
+	psubw	mm0,MMX_80w
+	punpcklbw mm1,mm7		;V (word)
+
+	psubw	mm1,MMX_80w
+	movq	mm2,mm0
+
+	pmullw	mm2,MMX_Ugrncoeff
+	movq	mm3,mm1
+
+	pmullw	mm3,MMX_Vgrncoeff
+	pmullw	mm0,MMX_Ublucoeff
+	pmullw	mm1,MMX_Vredcoeff
+	paddw	mm2,mm3
+
+	;mm0: blue
+	;mm1: red
+	;mm2: green
+
+	movq	mm6,[ecx+ebp*2]		;Y
+	pand	mm6,MMX_00FFw
+	psubw	mm6,MMX_10w
+	pmullw	mm6,MMX_Ycoeff
+	movq	mm4,mm6
+	paddw	mm6,mm0			;mm6: <B3><B2><B1><B0>
+	movq	mm5,mm4
+	paddw	mm4,mm1			;mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;mm5: <G3><G2><G1><G0>
+	psraw	mm6,6
+	psraw	mm4,6
+	packuswb mm6,mm6		;mm6: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm4,mm4		;mm4: R3R2R1R0R3R2R1R0
+	punpcklbw mm6,mm4		;mm6: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm4,mm6
+	punpcklbw mm6,mm5		;mm6: G1R1G1B2G0R0G0B0
+	punpckhbw mm4,mm5		;mm4: G3R3G3B3G2R2G2B2
+
+	movq	mm7,[ecx+ebp*2]		;Y
+	psrlw	mm7,8
+	psubw	mm7,MMX_10w
+	pmullw	mm7,MMX_Ycoeff
+	movq	mm3,mm7
+	paddw	mm7,mm0			;mm7: final blue
+	movq	mm5,mm3
+	paddw	mm3,mm1			;mm3: final red
+	paddw	mm5,mm2			;mm5: final green
+	psraw	mm7,6
+	psraw	mm3,6
+	packuswb mm7,mm7		;mm7: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm3,mm3		;mm3: R3R2R1R0R3R2R1R0
+	punpcklbw mm7,mm3		;mm7: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm3,mm7
+	punpcklbw mm7,mm5		;mm7: G1R1G1B2G0R0G0B0
+	punpckhbw mm3,mm5		;mm3: G3R3G3B3G2R2G2B2
+
+	;mm3	P7:P5
+	;mm4	P6:P4
+	;mm6	P2:P0
+	;mm7	P3:P1
+
+	movq	mm5,mm6
+	punpckldq mm5,mm7		;P1:P0
+	punpckhdq mm6,mm7		;P3:P2
+	movq	mm7,mm4
+	punpckldq mm4,mm3		;P5:P4
+	punpckhdq mm7,mm3		;P7:P6
+
+	movntq	[eax+ebp*8],mm5
+	movntq	[eax+ebp*8+8],mm6
+	movntq	[eax+ebp*8+16],mm4
+	movntq	[eax+ebp*8+24],mm7
+
+	movq	mm6,[edx+ebp*2]		;Y
+	pand	mm6,MMX_00FFw
+	psubw	mm6,MMX_10w
+	pmullw	mm6,MMX_Ycoeff
+	movq	mm4,mm6
+	paddw	mm6,mm0			;mm6: <B3><B2><B1><B0>
+	movq	mm5,mm4
+	paddw	mm4,mm1			;mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;mm5: <G3><G2><G1><G0>
+	psraw	mm6,6
+	psraw	mm4,6
+	packuswb mm6,mm6		;mm6: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm4,mm4		;mm4: R3R2R1R0R3R2R1R0
+	punpcklbw mm6,mm4		;mm6: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm4,mm6
+	punpcklbw mm6,mm5		;mm6: G1R1G1B2G0R0G0B0
+	punpckhbw mm4,mm5		;mm4: G3R3G3B3G2R2G2B2
+
+	movq	mm7,[edx+ebp*2]		;Y
+	psrlw	mm7,8
+	psubw	mm7,MMX_10w
+	pmullw	mm7,MMX_Ycoeff
+	movq	mm3,mm7
+	paddw	mm7,mm0			;mm7: final blue
+	movq	mm5,mm3
+	paddw	mm3,mm1			;mm3: final red
+	paddw	mm5,mm2			;mm5: final green
+	psraw	mm7,6
+	psraw	mm3,6
+	packuswb mm7,mm7		;mm7: B3B2B1B0B3B2B1B0
+	psraw	mm5,6
+	packuswb mm3,mm3		;mm3: R3R2R1R0R3R2R1R0
+	punpcklbw mm7,mm3		;mm7: R3B3R2B2R1B1R0B0
+	packuswb mm5,mm5		;mm5: G3G2G1G0G3G2G1G0
+	punpcklbw mm5,mm5		;mm5: G3G3G2G2G1G1G0G0
+	movq	mm3,mm7
+	punpcklbw mm7,mm5		;mm7: G1R1G1B2G0R0G0B0
+	punpckhbw mm3,mm5		;mm3: G3R3G3B3G2R2G2B2
+
+	;mm3	P7:P5
+	;mm4	P6:P4
+	;mm6	P2:P0
+	;mm7	P3:P1
+
+	movq	mm5,mm6
+	punpckldq mm5,mm7		;P1:P0
+	punpckhdq mm6,mm7		;P3:P2
+	movq	mm7,mm4
+	punpckldq mm4,mm3		;P5:P4
+	punpckhdq mm7,mm3		;P7:P6
+
+	movntq	[ebx+ebp*8   ],mm5
+	movntq	[ebx+ebp*8+ 8],mm6
+
+	movntq	[ebx+ebp*8+16],mm4
+	movntq	[ebx+ebp*8+24],mm7
+
+	add	ebp,4
+
+	jnz	col_loop_SSE
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+_asm_YUVtoRGB24_row_ISSE:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	add	eax,eax
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+	movd	mm0,esp
+	sub	esp,20
+	and	esp,-8
+	movd	dword ptr [esp+16],mm0
+
+col_loop_ISSE24:
+	prefetchnta	[esi+ebp+32]
+	prefetchnta [edi+ebp+32]
+	prefetchnta [ecx+ebp*2+32]
+	prefetchnta [edx+ebp*2+32]
+
+	movd		mm0, dword ptr [esi+ebp]	;U (byte)
+	pxor		mm7,mm7
+
+	movd		mm1, dword ptr [edi+ebp]	;V (byte)
+	punpcklbw mm0,mm7		;U (word)
+
+	movd		mm2, dword ptr [ecx+ebp*2]	;Y low
+	punpcklbw mm1,mm7		;V (word)
+
+	movd		mm3, dword ptr [edx+ebp*2]	;Y high
+	punpcklbw mm2,mm7		;Y1 (word)
+
+	psubw		mm2,MMX_10w
+	punpcklbw mm3,mm7		;Y2 (word)
+
+	psubw		mm3,MMX_10w
+
+	psubw		mm0,MMX_80w
+	psubw		mm1,MMX_80w
+
+	movq		[esp+0],mm0
+	movq		[esp+8],mm1
+
+	;group 1
+
+	pmullw		mm2,MMX_Ycoeff	;[lazy]
+	pmullw		mm3,MMX_Ycoeff	;[lazy]
+
+	pshufw		mm6,mm0,00000000b	;mm6 = U0U0U0U0
+	pshufw		mm7,mm1,00000000b	;mm7 = V0V0V0V0
+
+	pmullw		mm6,MMX_Ucoeff0
+	pshufw		mm4,mm2,01000000b	;mm4 = Y1Y0Y0Y0 [high]
+	pmullw		mm7,MMX_Vcoeff0
+	pshufw		mm5,mm3,01000000b	;mm4 = Y1Y0Y0Y0 [low]
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	;group 2
+
+	pshufw		mm6,[esp+0],01010000b	;mm6 = U1U1U0U0
+	pshufw		mm7,[esp+8],01010000b	;mm7 = V1V1V0V0
+
+	pmullw		mm6,MMX_Ucoeff1
+	pshufw		mm0,mm2,10100101b	;mm0 = Y2Y2Y1Y1		[high]
+	pmullw		mm7,MMX_Vcoeff1
+	pshufw		mm1,mm3,10100101b	;mm1 = Y2Y2Y1Y1		[low]
+
+	paddw		mm0,mm6
+	paddw		mm1,mm6
+	paddw		mm0,mm7
+	paddw		mm1,mm7
+
+	psraw		mm0,6
+	psraw		mm1,6
+
+	packuswb	mm4,mm0
+	packuswb	mm5,mm1
+
+	;group 3
+
+	pshufw		mm6,[esp+0],01010101b	;mm6 = U1U1U1U1
+	pshufw		mm7,[esp+8],01010101b	;mm7 = V1V1V1V1
+
+	movntq		[eax],mm4	;[lazy write]
+	movntq		[ebx],mm5	;[lazy write]
+
+	pmullw		mm6,MMX_Ucoeff2
+	pshufw		mm4,mm2,11111110b	;mm4 = Y3Y3Y3Y2		[high]
+	pmullw		mm7,MMX_Vcoeff2
+	pshufw		mm5,mm3,11111110b	;mm5 = Y3Y3Y3Y2		[low]
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	;next 3 groups
+
+	movd		mm2, dword ptr [ecx+ebp*2+4]	;Y low
+	pxor		mm7,mm7
+
+	movd		mm3, dword ptr [edx+ebp*2+4]	;Y high
+	punpcklbw	mm2,mm7		;U (word)
+
+	psubw		mm2,MMX_10w
+	punpcklbw	mm3,mm7		;V (word)
+
+	psubw		mm3,MMX_10w
+
+
+	;group 1
+
+	pmullw		mm2,MMX_Ycoeff	;[init]
+	pmullw		mm3,MMX_Ycoeff	;[init]
+
+	pshufw		mm6,[esp+0],10101010b	;mm6 = U2U2U2U2
+	pshufw		mm7,[esp+8],10101010b	;mm7 = V2V2V2V2
+
+	pmullw		mm6,MMX_Ucoeff0
+	pshufw		mm0,mm2,01000000b	;mm0 = Y1Y0Y0Y0 [high]
+	pmullw		mm7,MMX_Vcoeff0
+	pshufw		mm1,mm3,01000000b	;mm1 = Y1Y0Y0Y0 [low]
+
+	paddw		mm0,mm6
+	paddw		mm1,mm6
+	paddw		mm0,mm7
+	paddw		mm1,mm7
+
+	psraw		mm0,6
+	psraw		mm1,6
+
+	packuswb	mm4,mm0
+	packuswb	mm5,mm1
+
+	;group 2
+
+	pshufw		mm6,[esp+0],11111010b	;mm6 = U3U3U2U2
+	pshufw		mm7,[esp+8],11111010b	;mm7 = V3V3V2V2
+
+	movntq		[eax+8],mm4
+	movntq		[ebx+8],mm5
+
+	pmullw		mm6,MMX_Ucoeff1
+	pshufw		mm4,mm2,10100101b	;mm4 = Y2Y2Y1Y1		[high]
+	pmullw		mm7,MMX_Vcoeff1
+	pshufw		mm5,mm3,10100101b	;mm5 = Y2Y2Y1Y1		[low]
+
+	paddw		mm4,mm6
+	paddw		mm5,mm6
+	paddw		mm4,mm7
+	paddw		mm5,mm7
+
+	psraw		mm4,6
+	psraw		mm5,6
+
+	;group 3
+
+	pshufw		mm0,[esp+0],11111111b	;mm6 = U3U3U3U3
+	pshufw		mm1,[esp+8],11111111b	;mm7 = V3V3V3V3
+
+	pmullw		mm0,MMX_Ucoeff2
+	pshufw		mm2,mm2,11111110b	;mm6 = Y3Y3Y3Y2		[high]
+	pmullw		mm1,MMX_Vcoeff2
+	pshufw		mm3,mm3,11111110b	;mm7 = Y3Y3Y3Y2		[low]
+
+	paddw		mm2,mm0
+	paddw		mm3,mm0
+	paddw		mm2,mm1
+	paddw		mm3,mm1
+
+	psraw		mm2,6
+	psraw		mm3,6
+
+	packuswb	mm4,mm2
+	packuswb	mm5,mm3
+
+	movntq		[eax+16],mm4
+	add	eax,24
+	movntq		[ebx+16],mm5
+	add	ebx,24
+
+	;done
+
+	add	ebp,4
+	jnz	col_loop_ISSE24
+
+	mov	esp,[esp+16]
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+_asm_YUVtoRGB16_row_ISSE:
+	push	ebx
+	push	esi
+	push	edi
+	push	ebp
+
+	mov	eax,count
+	mov	ebp,eax
+	mov	ebx,eax
+	shl	ebx,2
+	add	eax,eax
+	add	ARGB1_pointer,ebx
+	add	ARGB2_pointer,ebx
+	add	Y1_pointer,eax
+	add	Y2_pointer,eax
+	add	U_pointer,ebp
+	add	V_pointer,ebp
+	neg	ebp
+
+	mov	esi,U_pointer
+	mov	edi,V_pointer
+	mov	ecx,Y1_pointer
+	mov	edx,Y2_pointer
+	mov	eax,ARGB1_pointer
+	mov	ebx,ARGB2_pointer
+
+col_loop_ISSE16:
+	prefetchnta [esi+ebp+32]
+	prefetchnta [edi+ebp+32]
+
+	movd	mm0, dword ptr [esi+ebp]		;[0       ] U (byte)
+	pxor	mm7,mm7			;[0      7] 
+
+	movd	mm1, dword ptr [edi+ebp]		;[01     7] V (byte)
+	punpcklbw mm0,mm7		;[01     7] U (word)
+
+	psubw	mm0,MMX_80w		;[01     7] 
+	punpcklbw mm1,mm7		;[01     7] V (word)
+
+	psubw	mm1,MMX_80w		;[01      ] 
+	movq	mm2,mm0			;[012     ] 
+
+	pmullw	mm2,MMX_Ugrncoeff	;[012     ] 
+	movq	mm3,mm1			;[0123    ] 
+
+	;mm0: blue
+	;mm1: red
+	;mm2: green
+
+	prefetchnta [ecx+ebp*2+32]
+	prefetchnta [edx+ebp*2+32]
+
+	movq	mm6,[ecx+ebp*2]		;[0123  6 ] [1] Y
+	;<-->
+
+	pmullw	mm3,MMX_Vgrncoeff	;[0123    ] 
+	movq	mm7,mm6			;[012   67] [2] Y
+
+	pmullw	mm0,MMX_Ublucoeff	;[0123    ] 
+	psrlw	mm7,8			;[012   67] [2]
+
+	pmullw	mm1,MMX_Vredcoeff	;[0123    ] 
+	;<-->
+
+	pand	mm6,MMX_00FFw		;[012   67] [1]
+	paddw	mm2,mm3			;[012   6 ] [C]
+
+	psubw	mm6,MMX_10w		;[012   67] [1]
+
+	pmullw	mm6,MMX_Ycoeff		;[012   67] [1]
+
+	psubw	mm7,MMX_10w		;[012   67] [2]
+	movq	mm4,mm6			;[012 4 67] [1]
+
+	pmullw	mm7,MMX_Ycoeff		;[012   67] [2]
+	movq	mm5,mm6			;[012 4567] [1]
+
+	paddw	mm6,mm0			;[012 4 67] [1] mm6: <B3><B2><B1><B0>
+	paddw	mm4,mm1			;[012 4567] [1] mm4: <R3><R2><R1><R0>
+
+	paddw	mm5,mm2			;[012 4567] [1] mm5: <G3><G2><G1><G0>
+	psraw	mm4,6			;[012 4567] [1]
+
+	movq	mm3,mm7			;[01234567] [2]
+	psraw	mm5,4			;[01234567] [1]
+
+	paddw	mm7,mm0			;[01234567] [2] mm6: <B3><B2><B1><B0>
+	psraw	mm6,6			;[01234567] [1]
+
+	paddsw	mm5,MMX_clip
+	packuswb mm6,mm6		;[01234567] [1] mm6: B3B2B1B0B3B2B1B0
+
+	psubusw	mm5,MMX_clip
+	packuswb mm4,mm4		;[01234567] [1] mm4: R3R2R1R0R3R2R1R0
+
+	pand	mm5,MMX_grnmask		;[01234567] [1] mm7: <G3><G2><G1><G0>
+	psrlq	mm6,2			;[01234567] [1]
+
+	punpcklbw mm6,mm4		;[0123 567] [1] mm4: R3B3R2B2R1B1R0B0
+
+	movq	mm4,[edx+ebp*2]		;[01234567] [3] Y
+	psrlw	mm6,1			;[01234567] [1]
+
+	pand	mm6,MMX_rbmask		;[01234567] [1] mm6: <RB3><RB2><RB1><RB0>
+
+	por	mm6,mm5			;[01234 67] [1] mm6: P6P4P2P0
+	movq	mm5,mm3			;[01234567] [2]
+
+	paddw	mm3,mm1			;[01234567] [2] mm4: <R3><R2><R1><R0>
+	paddw	mm5,mm2			;[01234567] [2] mm5: <G3><G2><G1><G0>
+
+	pand	mm4,MMX_00FFw		;[01234567] [3]
+	psraw	mm3,6			;[01234567] [2]	
+
+	psubw	mm4,MMX_10w		;[01234567] [3]
+	psraw	mm5,4			;[01234567] [2]
+
+	pmullw	mm4,MMX_Ycoeff		;[01234567] [3]
+	psraw	mm7,6			;[01234567] [2]
+
+	paddsw	mm5,MMX_clip
+	packuswb mm3,mm3		;[01234567] [2] mm4: R3R2R1R0R3R2R1R0
+
+	psubusw	mm5,MMX_clip
+	packuswb mm7,mm7		;[01234567] [2] mm6: B3B2B1B0B3B2B1B0
+
+	pand	mm5,MMX_grnmask		;[012 4567] [2] mm7: <G3><G2><G1><G0>
+	psrlq	mm7,2			;[01234567] [2]
+
+	punpcklbw mm7,mm3		;[012 4567] [2] mm6: R3B3R2B2R1B1R0B0
+
+	movq	mm3,[edx+ebp*2]		;[01234567] [4] Y
+	psrlw	mm7,1			;[01234567] [2]
+
+	pand	mm7,MMX_rbmask		;[01234567] [2] mm6: <RB3><RB2><RB1><RB0>
+	psrlw	mm3,8			;[01234567] [4]
+
+	por	mm7,mm5			;[01234567] [2] mm7: P7P5P3P1
+	movq	mm5,mm6			;[01234567] [A]
+
+	psubw	mm3,MMX_10w		;[01234567] [4]
+	punpcklwd mm6,mm7		;[01234567] [A] mm4: P3P2P1P0
+
+	pmullw	mm3,MMX_Ycoeff		;[0123456 ] [4]
+	punpckhwd mm5,mm7		;[0123456 ] [A} mm5: P7P6P5P4
+
+	movntq	[eax+ebp*4   ],mm6	;[012345  ] [A]
+	movq	mm6,mm4			;[0123456 ] [3]
+
+	movntq	[eax+ebp*4+ 8],mm5	;[0123456 ] [A]
+	paddw	mm6,mm0			;[01234 6 ] [3] mm6: <B3><B2><B1><B0>
+
+	movq	mm5,mm4			;[0123456 ] [3]
+	paddw	mm4,mm1			;[0123456 ] [3] mm4: <R3><R2><R1><R0>
+
+	paddw	mm5,mm2			;[0123456 ] [3] mm5: <G3><G2><G1><G0>
+	psraw	mm4,6			;[0123456 ] [3]
+
+	movq	mm7,mm3			;[01234567] [4]
+	psraw	mm5,4			;[01234567] [3]
+
+	paddw	mm7,mm0			;[01234567] [4] mm6: <B3><B2><B1><B0>
+	psraw	mm6,6			;[01234567] [3]
+
+	movq	mm0,mm3			;[01234567] [4]
+	packuswb mm4,mm4		;[01234567] [3] mm4: R3R2R1R0R3R2R1R0
+
+
+	packuswb mm6,mm6		;[01 34567] [3] mm6: B3B2B1B0B3B2B1B0
+	paddw	mm3,mm1			;[01234567] [4] mm4: <R3><R2><R1><R0>
+
+	psrlq	mm6,2
+	paddw	mm0,mm2			;[01 34567] [4] mm5: <G3><G2><G1><G0>
+
+	paddsw	mm5,MMX_clip
+	punpcklbw mm6,mm4		;[01 3 567] [3] mm6: B3B3B2B2B1B1B0B0
+
+	psubusw	mm5,MMX_clip
+	psrlw	mm6,1			;[01 3 567] [3]
+
+	pand	mm6,MMX_rbmask		;[01 3 567] [3] mm6: <B3><B2><B1><B0>
+	psraw	mm3,6			;[01 3 567] [4]
+
+	pand	mm5,MMX_grnmask		;[01 3 567] [3] mm7: <G3><G2><G1><G0>
+	psraw	mm0,4			;[01 3 567] [4]
+
+	por	mm6,mm5			;[01 3  67] [3] mm4: P6P4P2P0	
+	psraw	mm7,6			;[01 3  67] [4]
+
+	paddsw	mm0,MMX_clip
+	packuswb mm3,mm3		;[01 3  67] [4] mm4: R3R2R1R0R3R2R1R0
+
+	psubusw	mm0,MMX_clip
+	packuswb mm7,mm7		;[01 3  67] mm6: B3B2B1B0B3B2B1B0
+
+	pand	mm0,MMX_grnmask		;[01    67] mm7: <G3><G2><G1><G0>
+	psrlq	mm7,2
+
+	punpcklbw mm7,mm3		;[01    67] mm6: R3B3R2B2R1B1R0B0
+	movq	mm1,mm6
+
+	psrlw	mm7,1
+	add	ebp,4
+
+	pand	mm7,MMX_rbmask		;[01    67] mm6: <B3><B2><B1><B0>
+
+	por	mm0,mm7			;[01    67] mm0: P7P5P3P1
+
+	punpcklwd mm6,mm0		;[01    6 ] mm4: P3P2P1P0
+
+	punpckhwd mm1,mm0		;[ 1    6 ] mm5: P7P6P5P4
+	movntq	[ebx+ebp*4-16],mm6
+
+	movntq	[ebx+ebp*4- 8],mm1
+	jnz	col_loop_ISSE16
+
+	pop	ebp
+	pop	edi
+	pop	esi
+	pop	ebx
+	ret
+
+
+	end
diff --git a/vsfilter/dsutil/a_yuvtable.asm b/vsfilter/dsutil/a_yuvtable.asm
new file mode 100644
index 0000000000000000000000000000000000000000..5b8d1f3eca93764f0b9365fade715fb3ef57c72b
--- /dev/null
+++ b/vsfilter/dsutil/a_yuvtable.asm
@@ -0,0 +1,612 @@
+;	VirtualDub - Video processing and capture application
+;	Copyright (C) 1998-2001 Avery Lee
+;
+;	This program is free software; you can redistribute it and/or modify
+;	it under the terms of the GNU General Public License as published by
+;	the Free Software Foundation; either version 2 of the License, or
+;	(at your option) any later version.
+;
+;	This program is distributed in the hope that it will be useful,
+;	but WITHOUT ANY WARRANTY; without even the implied warranty of
+;	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;	GNU General Public License for more details.
+;
+;	You should have received a copy of the GNU General Public License
+;	along with this program; if not, write to the Free Software
+;	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	.486
+	.model	flat
+	.const
+
+	public _YUV_Y_table
+	public _YUV_U_table
+	public _YUV_V_table
+	public _YUV_clip_table
+	public _YUV_clip_table16
+
+_YUV_Y_table	dd	1FED1FEDh, 1FEF1FEFh, 1FF01FF0h, 1FF11FF1h
+		dd	1FF21FF2h, 1FF31FF3h, 1FF41FF4h, 1FF61FF6h
+		dd	1FF71FF7h, 1FF81FF8h, 1FF91FF9h, 1FFA1FFAh
+		dd	1FFB1FFBh, 1FFD1FFDh, 1FFE1FFEh, 1FFF1FFFh
+		dd	20002000h, 20012001h, 20022002h, 20032003h
+		dd	20052005h, 20062006h, 20072007h, 20082008h
+		dd	20092009h, 200A200Ah, 200C200Ch, 200D200Dh
+		dd	200E200Eh, 200F200Fh, 20102010h, 20112011h
+		dd	20132013h, 20142014h, 20152015h, 20162016h
+		dd	20172017h, 20182018h, 201A201Ah, 201B201Bh
+		dd	201C201Ch, 201D201Dh, 201E201Eh, 201F201Fh
+		dd	20212021h, 20222022h, 20232023h, 20242024h
+		dd	20252025h, 20262026h, 20282028h, 20292029h
+		dd	202A202Ah, 202B202Bh, 202C202Ch, 202D202Dh
+		dd	202F202Fh, 20302030h, 20312031h, 20322032h
+		dd	20332033h, 20342034h, 20362036h, 20372037h
+		dd	20382038h, 20392039h, 203A203Ah, 203B203Bh
+		dd	203D203Dh, 203E203Eh, 203F203Fh, 20402040h
+		dd	20412041h, 20422042h, 20442044h, 20452045h
+		dd	20462046h, 20472047h, 20482048h, 20492049h
+		dd	204A204Ah, 204C204Ch, 204D204Dh, 204E204Eh
+		dd	204F204Fh, 20502050h, 20512051h, 20532053h
+		dd	20542054h, 20552055h, 20562056h, 20572057h
+		dd	20582058h, 205A205Ah, 205B205Bh, 205C205Ch
+		dd	205D205Dh, 205E205Eh, 205F205Fh, 20612061h
+		dd	20622062h, 20632063h, 20642064h, 20652065h
+		dd	20662066h, 20682068h, 20692069h, 206A206Ah
+		dd	206B206Bh, 206C206Ch, 206D206Dh, 206F206Fh
+		dd	20702070h, 20712071h, 20722072h, 20732073h
+		dd	20742074h, 20762076h, 20772077h, 20782078h
+		dd	20792079h, 207A207Ah, 207B207Bh, 207D207Dh
+		dd	207E207Eh, 207F207Fh, 20802080h, 20812081h
+		dd	20822082h, 20842084h, 20852085h, 20862086h
+		dd	20872087h, 20882088h, 20892089h, 208B208Bh
+		dd	208C208Ch, 208D208Dh, 208E208Eh, 208F208Fh
+		dd	20902090h, 20922092h, 20932093h, 20942094h
+		dd	20952095h, 20962096h, 20972097h, 20982098h
+		dd	209A209Ah, 209B209Bh, 209C209Ch, 209D209Dh
+		dd	209E209Eh, 209F209Fh, 20A120A1h, 20A220A2h
+		dd	20A320A3h, 20A420A4h, 20A520A5h, 20A620A6h
+		dd	20A820A8h, 20A920A9h, 20AA20AAh, 20AB20ABh
+		dd	20AC20ACh, 20AD20ADh, 20AF20AFh, 20B020B0h
+		dd	20B120B1h, 20B220B2h, 20B320B3h, 20B420B4h
+		dd	20B620B6h, 20B720B7h, 20B820B8h, 20B920B9h
+		dd	20BA20BAh, 20BB20BBh, 20BD20BDh, 20BE20BEh
+		dd	20BF20BFh, 20C020C0h, 20C120C1h, 20C220C2h
+		dd	20C420C4h, 20C520C5h, 20C620C6h, 20C720C7h
+		dd	20C820C8h, 20C920C9h, 20CB20CBh, 20CC20CCh
+		dd	20CD20CDh, 20CE20CEh, 20CF20CFh, 20D020D0h
+		dd	20D220D2h, 20D320D3h, 20D420D4h, 20D520D5h
+		dd	20D620D6h, 20D720D7h, 20D920D9h, 20DA20DAh
+		dd	20DB20DBh, 20DC20DCh, 20DD20DDh, 20DE20DEh
+		dd	20DF20DFh, 20E120E1h, 20E220E2h, 20E320E3h
+		dd	20E420E4h, 20E520E5h, 20E620E6h, 20E820E8h
+		dd	20E920E9h, 20EA20EAh, 20EB20EBh, 20EC20ECh
+		dd	20ED20EDh, 20EF20EFh, 20F020F0h, 20F120F1h
+		dd	20F220F2h, 20F320F3h, 20F420F4h, 20F620F6h
+		dd	20F720F7h, 20F820F8h, 20F920F9h, 20FA20FAh
+		dd	20FB20FBh, 20FD20FDh, 20FE20FEh, 20FF20FFh
+		dd	21002100h, 21012101h, 21022102h, 21042104h
+		dd	21052105h, 21062106h, 21072107h, 21082108h
+		dd	21092109h, 210B210Bh, 210C210Ch, 210D210Dh
+		dd	210E210Eh, 210F210Fh, 21102110h, 21122112h
+		dd	21132113h, 21142114h, 21152115h, 21162116h
+
+_YUV_U_table	dd	1EFE1032h, 1F001032h, 1F021031h, 1F041031h
+		dd	1F061030h, 1F081030h, 1F0A1030h, 1F0C102Fh
+		dd	1F0E102Fh, 1F10102Fh, 1F12102Eh, 1F14102Eh
+		dd	1F16102Dh, 1F18102Dh, 1F1A102Dh, 1F1C102Ch
+		dd	1F1E102Ch, 1F20102Bh, 1F22102Bh, 1F24102Bh
+		dd	1F26102Ah, 1F28102Ah, 1F2A1029h, 1F2C1029h
+		dd	1F2E1029h, 1F301028h, 1F321028h, 1F341027h
+		dd	1F361027h, 1F381027h, 1F3A1026h, 1F3C1026h
+		dd	1F3E1026h, 1F401025h, 1F421025h, 1F441024h
+		dd	1F461024h, 1F481024h, 1F4A1023h, 1F4C1023h
+		dd	1F4E1022h, 1F501022h, 1F521022h, 1F541021h
+		dd	1F561021h, 1F591020h, 1F5B1020h, 1F5D1020h
+		dd	1F5F101Fh, 1F61101Fh, 1F63101Eh, 1F65101Eh
+		dd	1F67101Eh, 1F69101Dh, 1F6B101Dh, 1F6D101Dh
+		dd	1F6F101Ch, 1F71101Ch, 1F73101Bh, 1F75101Bh
+		dd	1F77101Bh, 1F79101Ah, 1F7B101Ah, 1F7D1019h
+		dd	1F7F1019h, 1F811019h, 1F831018h, 1F851018h
+		dd	1F871017h, 1F891017h, 1F8B1017h, 1F8D1016h
+		dd	1F8F1016h, 1F911016h, 1F931015h, 1F951015h
+		dd	1F971014h, 1F991014h, 1F9B1014h, 1F9D1013h
+		dd	1F9F1013h, 1FA11012h, 1FA31012h, 1FA51012h
+		dd	1FA71011h, 1FA91011h, 1FAB1010h, 1FAD1010h
+		dd	1FAF1010h, 1FB1100Fh, 1FB3100Fh, 1FB5100Eh
+		dd	1FB7100Eh, 1FB9100Eh, 1FBB100Dh, 1FBD100Dh
+		dd	1FBF100Dh, 1FC1100Ch, 1FC3100Ch, 1FC5100Bh
+		dd	1FC7100Bh, 1FCA100Bh, 1FCC100Ah, 1FCE100Ah
+		dd	1FD01009h, 1FD21009h, 1FD41009h, 1FD61008h
+		dd	1FD81008h, 1FDA1007h, 1FDC1007h, 1FDE1007h
+		dd	1FE01006h, 1FE21006h, 1FE41005h, 1FE61005h
+		dd	1FE81005h, 1FEA1004h, 1FEC1004h, 1FEE1004h
+		dd	1FF01003h, 1FF21003h, 1FF41002h, 1FF61002h
+		dd	1FF81002h, 1FFA1001h, 1FFC1001h, 1FFE1000h
+		dd	20001000h, 20021000h, 20040FFFh, 20060FFFh
+		dd	20080FFEh, 200A0FFEh, 200C0FFEh, 200E0FFDh
+		dd	20100FFDh, 20120FFCh, 20140FFCh, 20160FFCh
+		dd	20180FFBh, 201A0FFBh, 201C0FFBh, 201E0FFAh
+		dd	20200FFAh, 20220FF9h, 20240FF9h, 20260FF9h
+		dd	20280FF8h, 202A0FF8h, 202C0FF7h, 202E0FF7h
+		dd	20300FF7h, 20320FF6h, 20340FF6h, 20360FF5h
+		dd	20390FF5h, 203B0FF5h, 203D0FF4h, 203F0FF4h
+		dd	20410FF3h, 20430FF3h, 20450FF3h, 20470FF2h
+		dd	20490FF2h, 204B0FF2h, 204D0FF1h, 204F0FF1h
+		dd	20510FF0h, 20530FF0h, 20550FF0h, 20570FEFh
+		dd	20590FEFh, 205B0FEEh, 205D0FEEh, 205F0FEEh
+		dd	20610FEDh, 20630FEDh, 20650FECh, 20670FECh
+		dd	20690FECh, 206B0FEBh, 206D0FEBh, 206F0FEAh
+		dd	20710FEAh, 20730FEAh, 20750FE9h, 20770FE9h
+		dd	20790FE9h, 207B0FE8h, 207D0FE8h, 207F0FE7h
+		dd	20810FE7h, 20830FE7h, 20850FE6h, 20870FE6h
+		dd	20890FE5h, 208B0FE5h, 208D0FE5h, 208F0FE4h
+		dd	20910FE4h, 20930FE3h, 20950FE3h, 20970FE3h
+		dd	20990FE2h, 209B0FE2h, 209D0FE2h, 209F0FE1h
+		dd	20A10FE1h, 20A30FE0h, 20A50FE0h, 20A70FE0h
+		dd	20AA0FDFh, 20AC0FDFh, 20AE0FDEh, 20B00FDEh
+		dd	20B20FDEh, 20B40FDDh, 20B60FDDh, 20B80FDCh
+		dd	20BA0FDCh, 20BC0FDCh, 20BE0FDBh, 20C00FDBh
+		dd	20C20FDAh, 20C40FDAh, 20C60FDAh, 20C80FD9h
+		dd	20CA0FD9h, 20CC0FD9h, 20CE0FD8h, 20D00FD8h
+		dd	20D20FD7h, 20D40FD7h, 20D60FD7h, 20D80FD6h
+		dd	20DA0FD6h, 20DC0FD5h, 20DE0FD5h, 20E00FD5h
+		dd	20E20FD4h, 20E40FD4h, 20E60FD3h, 20E80FD3h
+		dd	20EA0FD3h, 20EC0FD2h, 20EE0FD2h, 20F00FD1h
+		dd	20F20FD1h, 20F40FD1h, 20F60FD0h, 20F80FD0h
+		dd	20FA0FD0h, 20FC0FCFh, 20FE0FCFh, 21000FCEh
+
+_YUV_V_table	dd	1F341068h, 1F351067h, 1F371066h, 1F391066h
+		dd	1F3A1065h, 1F3C1064h, 1F3D1063h, 1F3F1062h
+		dd	1F401062h, 1F421061h, 1F441060h, 1F45105Fh
+		dd	1F47105Eh, 1F48105Dh, 1F4A105Dh, 1F4C105Ch
+		dd	1F4D105Bh, 1F4F105Ah, 1F501059h, 1F521059h
+		dd	1F541058h, 1F551057h, 1F571056h, 1F581055h
+		dd	1F5A1055h, 1F5C1054h, 1F5D1053h, 1F5F1052h
+		dd	1F601051h, 1F621050h, 1F641050h, 1F65104Fh
+		dd	1F67104Eh, 1F68104Dh, 1F6A104Ch, 1F6C104Ch
+		dd	1F6D104Bh, 1F6F104Ah, 1F701049h, 1F721048h
+		dd	1F741048h, 1F751047h, 1F771046h, 1F781045h
+		dd	1F7A1044h, 1F7C1043h, 1F7D1043h, 1F7F1042h
+		dd	1F801041h, 1F821040h, 1F84103Fh, 1F85103Fh
+		dd	1F87103Eh, 1F88103Dh, 1F8A103Ch, 1F8B103Bh
+		dd	1F8D103Bh, 1F8F103Ah, 1F901039h, 1F921038h
+		dd	1F931037h, 1F951036h, 1F971036h, 1F981035h
+		dd	1F9A1034h, 1F9B1033h, 1F9D1032h, 1F9F1032h
+		dd	1FA01031h, 1FA21030h, 1FA3102Fh, 1FA5102Eh
+		dd	1FA7102Eh, 1FA8102Dh, 1FAA102Ch, 1FAB102Bh
+		dd	1FAD102Ah, 1FAF1029h, 1FB01029h, 1FB21028h
+		dd	1FB31027h, 1FB51026h, 1FB71025h, 1FB81025h
+		dd	1FBA1024h, 1FBB1023h, 1FBD1022h, 1FBF1021h
+		dd	1FC01021h, 1FC21020h, 1FC3101Fh, 1FC5101Eh
+		dd	1FC7101Dh, 1FC8101Ch, 1FCA101Ch, 1FCB101Bh
+		dd	1FCD101Ah, 1FCF1019h, 1FD01018h, 1FD21018h
+		dd	1FD31017h, 1FD51016h, 1FD71015h, 1FD81014h
+		dd	1FDA1014h, 1FDB1013h, 1FDD1012h, 1FDE1011h
+		dd	1FE01010h, 1FE2100Fh, 1FE3100Fh, 1FE5100Eh
+		dd	1FE6100Dh, 1FE8100Ch, 1FEA100Bh, 1FEB100Bh
+		dd	1FED100Ah, 1FEE1009h, 1FF01008h, 1FF21007h
+		dd	1FF31007h, 1FF51006h, 1FF61005h, 1FF81004h
+		dd	1FFA1003h, 1FFB1002h, 1FFD1002h, 1FFE1001h
+		dd	20001000h, 20020FFFh, 20030FFEh, 20050FFEh
+		dd	20060FFDh, 20080FFCh, 200A0FFBh, 200B0FFAh
+		dd	200D0FF9h, 200E0FF9h, 20100FF8h, 20120FF7h
+		dd	20130FF6h, 20150FF5h, 20160FF5h, 20180FF4h
+		dd	201A0FF3h, 201B0FF2h, 201D0FF1h, 201E0FF1h
+		dd	20200FF0h, 20220FEFh, 20230FEEh, 20250FEDh
+		dd	20260FECh, 20280FECh, 20290FEBh, 202B0FEAh
+		dd	202D0FE9h, 202E0FE8h, 20300FE8h, 20310FE7h
+		dd	20330FE6h, 20350FE5h, 20360FE4h, 20380FE4h
+		dd	20390FE3h, 203B0FE2h, 203D0FE1h, 203E0FE0h
+		dd	20400FDFh, 20410FDFh, 20430FDEh, 20450FDDh
+		dd	20460FDCh, 20480FDBh, 20490FDBh, 204B0FDAh
+		dd	204D0FD9h, 204E0FD8h, 20500FD7h, 20510FD7h
+		dd	20530FD6h, 20550FD5h, 20560FD4h, 20580FD3h
+		dd	20590FD2h, 205B0FD2h, 205D0FD1h, 205E0FD0h
+		dd	20600FCFh, 20610FCEh, 20630FCEh, 20650FCDh
+		dd	20660FCCh, 20680FCBh, 20690FCAh, 206B0FCAh
+		dd	206D0FC9h, 206E0FC8h, 20700FC7h, 20710FC6h
+		dd	20730FC5h, 20750FC5h, 20760FC4h, 20780FC3h
+		dd	20790FC2h, 207B0FC1h, 207C0FC1h, 207E0FC0h
+		dd	20800FBFh, 20810FBEh, 20830FBDh, 20840FBDh
+		dd	20860FBCh, 20880FBBh, 20890FBAh, 208B0FB9h
+		dd	208C0FB8h, 208E0FB8h, 20900FB7h, 20910FB6h
+		dd	20930FB5h, 20940FB4h, 20960FB4h, 20980FB3h
+		dd	20990FB2h, 209B0FB1h, 209C0FB0h, 209E0FB0h
+		dd	20A00FAFh, 20A10FAEh, 20A30FADh, 20A40FACh
+		dd	20A60FABh, 20A80FABh, 20A90FAAh, 20AB0FA9h
+		dd	20AC0FA8h, 20AE0FA7h, 20B00FA7h, 20B10FA6h
+		dd	20B30FA5h, 20B40FA4h, 20B60FA3h, 20B80FA3h
+		dd	20B90FA2h, 20BB0FA1h, 20BC0FA0h, 20BE0F9Fh
+		dd	20C00F9Eh, 20C10F9Eh, 20C30F9Dh, 20C40F9Ch
+		dd	20C60F9Bh, 20C70F9Ah, 20C90F9Ah, 20CB0F99h
+
+_YUV_clip_table	db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 1, 2, 3
+		db	 4, 5, 6, 7
+		db	 8, 9, 10, 11
+		db	 12, 13, 14, 15
+		db	 16, 17, 18, 19
+		db	 20, 21, 22, 23
+		db	 24, 25, 26, 27
+		db	 28, 29, 30, 31
+		db	 32, 33, 34, 35
+		db	 36, 37, 38, 39
+		db	 40, 41, 42, 43
+		db	 44, 45, 46, 47
+		db	 48, 49, 50, 51
+		db	 52, 53, 54, 55
+		db	 56, 57, 58, 59
+		db	 60, 61, 62, 63
+		db	 64, 65, 66, 67
+		db	 68, 69, 70, 71
+		db	 72, 73, 74, 75
+		db	 76, 77, 78, 79
+		db	 80, 81, 82, 83
+		db	 84, 85, 86, 87
+		db	 88, 89, 90, 91
+		db	 92, 93, 94, 95
+		db	 96, 97, 98, 99
+		db	 100, 101, 102, 103
+		db	 104, 105, 106, 107
+		db	 108, 109, 110, 111
+		db	 112, 113, 114, 115
+		db	 116, 117, 118, 119
+		db	 120, 121, 122, 123
+		db	 124, 125, 126, 127
+		db	 128, 129, 130, 131
+		db	 132, 133, 134, 135
+		db	 136, 137, 138, 139
+		db	 140, 141, 142, 143
+		db	 144, 145, 146, 147
+		db	 148, 149, 150, 151
+		db	 152, 153, 154, 155
+		db	 156, 157, 158, 159
+		db	 160, 161, 162, 163
+		db	 164, 165, 166, 167
+		db	 168, 169, 170, 171
+		db	 172, 173, 174, 175
+		db	 176, 177, 178, 179
+		db	 180, 181, 182, 183
+		db	 184, 185, 186, 187
+		db	 188, 189, 190, 191
+		db	 192, 193, 194, 195
+		db	 196, 197, 198, 199
+		db	 200, 201, 202, 203
+		db	 204, 205, 206, 207
+		db	 208, 209, 210, 211
+		db	 212, 213, 214, 215
+		db	 216, 217, 218, 219
+		db	 220, 221, 222, 223
+		db	 224, 225, 226, 227
+		db	 228, 229, 230, 231
+		db	 232, 233, 234, 235
+		db	 236, 237, 238, 239
+		db	 240, 241, 242, 243
+		db	 244, 245, 246, 247
+		db	 248, 249, 250, 251
+		db	 252, 253, 254, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+		db	 255, 255, 255, 255
+
+
+_YUV_clip_table16	db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 0, 0, 0, 0
+		db	 1, 1, 1, 1
+		db	 1, 1, 1, 1
+		db	 2, 2, 2, 2
+		db	 2, 2, 2, 2
+		db	 3, 3, 3, 3
+		db	 3, 3, 3, 3
+		db	 4, 4, 4, 4
+		db	 4, 4, 4, 4
+		db	 5, 5, 5, 5
+		db	 5, 5, 5, 5
+		db	 6, 6, 6, 6
+		db	 6, 6, 6, 6
+		db	 7, 7, 7, 7
+		db	 7, 7, 7, 7
+		db	 8, 8, 8, 8
+		db	 8, 8, 8, 8
+		db	 9, 9, 9, 9
+		db	 9, 9, 9, 9
+		db	 10, 10, 10, 10
+		db	 10, 10, 10, 10
+		db	 11, 11, 11, 11
+		db	 11, 11, 11, 11
+		db	 12, 12, 12, 12
+		db	 12, 12, 12, 12
+		db	 13, 13, 13, 13
+		db	 13, 13, 13, 13
+		db	 14, 14, 14, 14
+		db	 14, 14, 14, 14
+		db	 15, 15, 15, 15
+		db	 15, 15, 15, 15
+		db	 16, 16, 16, 16
+		db	 16, 16, 16, 16
+		db	 17, 17, 17, 17
+		db	 17, 17, 17, 17
+		db	 18, 18, 18, 18
+		db	 18, 18, 18, 18
+		db	 19, 19, 19, 19
+		db	 19, 19, 19, 19
+		db	 20, 20, 20, 20
+		db	 20, 20, 20, 20
+		db	 21, 21, 21, 21
+		db	 21, 21, 21, 21
+		db	 22, 22, 22, 22
+		db	 22, 22, 22, 22
+		db	 23, 23, 23, 23
+		db	 23, 23, 23, 23
+		db	 24, 24, 24, 24
+		db	 24, 24, 24, 24
+		db	 25, 25, 25, 25
+		db	 25, 25, 25, 25
+		db	 26, 26, 26, 26
+		db	 26, 26, 26, 26
+		db	 27, 27, 27, 27
+		db	 27, 27, 27, 27
+		db	 28, 28, 28, 28
+		db	 28, 28, 28, 28
+		db	 29, 29, 29, 29
+		db	 29, 29, 29, 29
+		db	 30, 30, 30, 30
+		db	 30, 30, 30, 30
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+		db	 31, 31, 31, 31
+
+
+	end
+
diff --git a/vsfilter/dsutil/convert_a.asm b/vsfilter/dsutil/convert_a.asm
new file mode 100644
index 0000000000000000000000000000000000000000..d57b207e9e006617e1463f3c16af5c578feef93c
--- /dev/null
+++ b/vsfilter/dsutil/convert_a.asm
@@ -0,0 +1,263 @@
+; Avisynth v2.5.  Copyright 2002 Ben Rudiak-Gould et al.
+; http://www.avisynth.org
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, write to the Free Software
+; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
+; http://www.gnu.org/copyleft/gpl.html .
+;
+; Linking Avisynth statically or dynamically with other modules is making a
+; combined work based on Avisynth.  Thus, the terms and conditions of the GNU
+; General Public License cover the whole combination.
+;
+; As a special exception, the copyright holders of Avisynth give you
+; permission to link Avisynth with independent modules that communicate with
+; Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
+; terms of these independent modules, and to copy and distribute the
+; resulting combined work under terms of your choice, provided that
+; every copy of the combined work is accompanied by a complete copy of
+; the source code of Avisynth (the version of Avisynth used to produce the
+; combined work), being distributed under the terms of the GNU General
+; Public License plus this exception.  An independent module is a module
+; which is not derived from or based on Avisynth, such as 3rd-party filters,
+; import and export plugins, or graphical user interfaces.
+
+	.586
+	.mmx
+	.model	flat
+
+; alignment has to be 'page' so that I can use 'align 32' below
+
+_TEXT64	segment	page public use32 'CODE'
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+	align	8
+
+yuv2rgb_constants:
+
+x0000_0000_0010_0010	dq	00000000000100010h
+x0080_0080_0080_0080	dq	00080008000800080h
+x00FF_00FF_00FF_00FF	dq	000FF00FF00FF00FFh
+x00002000_00002000	dq	00000200000002000h
+xFF000000_FF000000	dq	0FF000000FF000000h
+cy			dq	000004A8500004A85h
+crv			dq	03313000033130000h
+cgu_cgv			dq	0E5FCF377E5FCF377h
+cbu			dq	00000408D0000408Dh
+
+yuv2rgb_constants_rec709:
+
+	dq	00000000000100010h
+	dq	00080008000800080h
+	dq	000FF00FF00FF00FFh
+	dq	00000200000002000h
+	dq	0FF000000FF000000h
+	dq	000004A8500004A85h
+	dq	03960000039600000h
+	dq	0EEF5F930EEF5F930h
+	dq	00000439B0000439Bh
+
+ofs_x0000_0000_0010_0010 = 0
+ofs_x0080_0080_0080_0080 = 8
+ofs_x00FF_00FF_00FF_00FF = 16
+ofs_x00002000_00002000 = 24
+ofs_xFF000000_FF000000 = 32
+ofs_cy = 40
+ofs_crv = 48
+ofs_cgu_cgv = 56
+ofs_cbu = 64
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+GET_Y	MACRO	mma,uyvy
+IF &uyvy
+	psrlw		mma,8
+ELSE
+	pand		mma,[edx+ofs_x00FF_00FF_00FF_00FF]
+ENDIF
+	ENDM
+
+GET_UV	MACRO	mma,uyvy
+	GET_Y		mma,1-uyvy
+	ENDM
+
+YUV2RGB_INNER_LOOP	MACRO	uyvy,rgb32,no_next_pixel
+
+;; This YUV422->RGB conversion code uses only four MMX registers per
+;; source dword, so I convert two dwords in parallel.  Lines corresponding
+;; to the "second pipe" are indented an extra space.  There's almost no
+;; overlap, except at the end and in the three lines marked ***.
+;; revised 4july,2002 to properly set alpha in rgb32 to default "on" & other small memory optimizations
+
+	movd		mm0, dword ptr [esi]
+	 movd		 mm5, dword ptr [esi+4]
+	movq		mm1,mm0
+	GET_Y		mm0,&uyvy	; mm0 = __________Y1__Y0
+	 movq		 mm4,mm5
+	GET_UV		mm1,&uyvy	; mm1 = __________V0__U0
+	 GET_Y		 mm4,&uyvy
+	movq		mm2,mm5		; *** avoid reload from [esi+4]
+	 GET_UV		 mm5,&uyvy
+	psubw		mm0, qword ptr [edx+ofs_x0000_0000_0010_0010]
+	 movd		 mm6, dword ptr [esi+8-4*(no_next_pixel)]
+	GET_UV		mm2,&uyvy	; mm2 = __________V2__U2
+	 psubw		 mm4, qword ptr [edx+ofs_x0000_0000_0010_0010]
+	paddw		mm2,mm1
+	 GET_UV		 mm6,&uyvy
+	psubw		mm1, qword ptr [edx+ofs_x0080_0080_0080_0080]
+	 paddw		 mm6,mm5
+	psllq		mm2,32
+	 psubw		 mm5, qword ptr [edx+ofs_x0080_0080_0080_0080]
+	punpcklwd	mm0,mm2		; mm0 = ______Y1______Y0
+	 psllq		 mm6,32
+	pmaddwd		mm0, qword ptr [edx+ofs_cy]
+	 punpcklwd	 mm4,mm6
+	paddw		mm1,mm1
+	 pmaddwd	 mm4, qword ptr [edx+ofs_cy]
+	 paddw		 mm5,mm5
+	paddw		mm1,mm2		; mm1 = __V1__U1__V0__U0 * 2
+	paddd		mm0,[edx+ofs_x00002000_00002000]
+	 paddw		 mm5,mm6
+	movq		mm2,mm1
+	 paddd		 mm4,[edx+ofs_x00002000_00002000]
+	movq		mm3,mm1
+	 movq		 mm6,mm5
+	pmaddwd		mm1,[edx+ofs_crv]
+	 movq		 mm7,mm5
+	paddd		mm1,mm0
+	 pmaddwd	 mm5,[edx+ofs_crv]
+	psrad		mm1,14		; mm1 = RRRRRRRRrrrrrrrr
+	 paddd		 mm5,mm4
+	pmaddwd		mm2,[edx+ofs_cgu_cgv]
+	 psrad		 mm5,14
+	paddd		mm2,mm0
+	 pmaddwd	 mm6,[edx+ofs_cgu_cgv]
+	psrad		mm2,14		; mm2 = GGGGGGGGgggggggg
+	 paddd		 mm6,mm4
+	pmaddwd		mm3,[edx+ofs_cbu]
+	 psrad		 mm6,14
+	paddd		mm3,mm0
+	 pmaddwd	 mm7,[edx+ofs_cbu]
+       add	       esi,8
+       add	       edi,12+4*rgb32
+IFE &no_next_pixel
+       cmp	       esi,ecx
+ENDIF
+	psrad		mm3,14		; mm3 = BBBBBBBBbbbbbbbb
+	 paddd		 mm7,mm4
+	pxor		mm0,mm0
+	 psrad		 mm7,14
+	packssdw	mm3,mm2	; mm3 = GGGGggggBBBBbbbb
+	 packssdw	 mm7,mm6
+	packssdw	mm1,mm0	; mm1 = ________RRRRrrrr
+	 packssdw	 mm5,mm0	; *** avoid pxor mm4,mm4
+	movq		mm2,mm3
+	 movq		 mm6,mm7
+	punpcklwd	mm2,mm1	; mm2 = RRRRBBBBrrrrbbbb
+	 punpcklwd	 mm6,mm5
+	punpckhwd	mm3,mm1	; mm3 = ____GGGG____gggg
+	 punpckhwd	 mm7,mm5
+	movq		mm0,mm2
+	 movq		 mm4,mm6
+	punpcklwd	mm0,mm3	; mm0 = ____rrrrggggbbbb
+	 punpcklwd	 mm4,mm7
+IFE &rgb32
+	psllq		mm0,16
+	 psllq		 mm4,16
+ENDIF
+	punpckhwd	mm2,mm3	; mm2 = ____RRRRGGGGBBBB
+	 punpckhwd	 mm6,mm7
+	packuswb	mm0,mm2	; mm0 = __RRGGBB__rrggbb <- ta dah!
+	 packuswb	 mm4,mm6
+
+IF &rgb32
+	por mm0, [edx+ofs_xFF000000_FF000000]	 ; set alpha channels "on"
+	 por mm4, [edx+ofs_xFF000000_FF000000]
+	movq	[edi-16],mm0	; store the quadwords independently
+	 movq	 [edi-8],mm4
+ELSE
+	psrlq	mm0,8		; pack the two quadwords into 12 bytes
+	psllq	mm4,8		; (note: the two shifts above leave
+	movd	dword ptr [edi-12],mm0	; mm0,4 = __RRGGBBrrggbb__)
+	psrlq	mm0,32
+	por	mm4,mm0
+	movd	dword ptr [edi-8],mm4
+	psrlq	mm4,32
+	movd	dword ptr [edi-4],mm4
+ENDIF
+
+	ENDM
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+YUV2RGB_PROC	MACRO	procname,uyvy,rgb32
+
+	PUBLIC	C _&procname
+
+;;void __cdecl procname(
+;;	[esp+ 4] const BYTE* src,
+;;	[esp+ 8] BYTE* dst,
+;;	[esp+12] const BYTE* src_end,
+;;	[esp+16] int src_pitch,
+;;	[esp+20] int row_size,
+;;	[esp+24] bool rec709);
+
+_&procname	PROC
+
+	push	esi
+	push	edi
+	push	ebx
+
+	mov	eax,[esp+16+12]
+	mov	esi,[esp+12+12]		; read source bottom-up
+	mov	edi,[esp+8+12]
+	mov	ebx,[esp+20+12]
+	mov	edx,offset yuv2rgb_constants
+	test	byte ptr [esp+24+12],1
+	jz	loop0
+	mov	edx,offset yuv2rgb_constants_rec709
+
+loop0:
+	sub	esi,eax
+	lea	ecx,[esi+ebx-8]
+
+	align 32
+loop1:
+	YUV2RGB_INNER_LOOP	uyvy,rgb32,0
+	jb	loop1
+
+	YUV2RGB_INNER_LOOP	uyvy,rgb32,1
+
+	sub	esi,ebx
+	cmp	esi,[esp+4+12]
+	ja	loop0
+
+	emms
+	pop	ebx
+	pop	edi
+	pop	esi
+	retn
+
+_&procname	ENDP
+
+	ENDM
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+YUV2RGB_PROC	mmx_YUY2toRGB24,0,0
+YUV2RGB_PROC	mmx_YUY2toRGB32,0,1
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+	END
diff --git a/vsfilter/dsutil/dsutil.vcproj b/vsfilter/dsutil/dsutil.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..ed3e2f921ab6a260d74e47b01f4265434eaf4f3f
--- /dev/null
+++ b/vsfilter/dsutil/dsutil.vcproj
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="dsutil"
+	ProjectGUID="{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}"
+	RootNamespace="dsutil"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="vfw32.lib Version.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="vfw32.lib Version.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<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=".\a_yuv2rgb.asm"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\a_yuvtable.asm"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\convert_a.asm"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCustomBuildTool"
+						CommandLine="ml /c /coff /Cx /nologo /Fo&quot;$(OutDir)\$(InputName).obj&quot; &quot;$(InputPath)"
+						Outputs="$(OutDir)\$(InputName).obj"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\DSMPropertyBag.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DSUtil.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\FontInstaller.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\MediaTypeEx.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\MediaTypes.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\NullRenderers.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\text.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\vd.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\DSMPropertyBag.h"
+				>
+			</File>
+			<File
+				RelativePath=".\DSUtil.h"
+				>
+			</File>
+			<File
+				RelativePath=".\FontInstaller.h"
+				>
+			</File>
+			<File
+				RelativePath=".\MediaTypeEx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\MediaTypes.h"
+				>
+			</File>
+			<File
+				RelativePath=".\NullRenderers.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\text.h"
+				>
+			</File>
+			<File
+				RelativePath=".\vd.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/vsfilter/dsutil/stdafx.cpp b/vsfilter/dsutil/stdafx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d90499c57cf00b6bad1a0ff199f1261c80330043
--- /dev/null
+++ b/vsfilter/dsutil/stdafx.cpp
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// dsutil.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/vsfilter/dsutil/stdafx.h b/vsfilter/dsutil/stdafx.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b8b56f6a3585a8f620a5dba5854191c0b3ef01f
--- /dev/null
+++ b/vsfilter/dsutil/stdafx.h
@@ -0,0 +1,47 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC core and standard components
+
+// TODO: reference additional headers your program requires here
+
+#include <Shlwapi.h>
+
+#include <atlcoll.h>
+#include <atlpath.h>
+
+#include <streams.h>
+#include <dvdmedia.h>
diff --git a/vsfilter/dsutil/text.cpp b/vsfilter/dsutil/text.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f750a91b4251975e8f8a0f3859f9b387ba60eb7b
--- /dev/null
+++ b/vsfilter/dsutil/text.cpp
@@ -0,0 +1,220 @@
+#include "stdafx.h"
+#include "text.h"
+
+/*
+CString Explode(CString str, CAtlList<CString>& sl, TCHAR sep, int limit)
+{
+	sl.RemoveAll();
+
+	if(limit == 1) {sl.AddTail(str); return _T("");}
+
+	if(!str.IsEmpty() && str[str.GetLength()-1] != sep)
+		str += sep;
+
+	for(int i = 0, j = 0; (j = str.Find(sep, i)) >= 0; i = j+1)
+	{
+		CString tmp = str.Mid(i, j-i);
+		tmp.TrimLeft(sep); tmp.TrimRight(sep);
+		tmp.TrimLeft(); tmp.TrimRight();
+		sl.AddTail(tmp);
+		if(limit > 0 && sl.GetCount() == limit-1)
+		{
+			if(j+1 < str.GetLength()) 
+			{
+				CString tmp = str.Mid(j+1);
+				tmp.TrimLeft(sep); tmp.TrimRight(sep);
+				tmp.TrimLeft(); tmp.TrimRight();
+				sl.AddTail(tmp);
+			}
+			break;
+		}
+	}
+
+	if(sl.IsEmpty())
+	{
+		str.TrimLeft(sep); str.TrimRight(sep);
+		str.TrimLeft(); str.TrimRight();
+		sl.AddTail(str);
+	}
+
+	return sl.GetHead();
+}
+
+CString ExplodeMin(CString str, CAtlList<CString>& sl, TCHAR sep, int limit)
+{
+	Explode(str, sl, sep, limit);
+	POSITION pos = sl.GetHeadPosition();
+	while(pos) 
+	{
+		POSITION tmp = pos;
+		if(sl.GetNext(pos).IsEmpty())
+			sl.RemoveAt(tmp);
+	}
+	if(sl.IsEmpty()) sl.AddTail(CString()); // eh
+
+	return sl.GetHead();
+}
+
+CString Implode(CAtlList<CString>& sl, TCHAR sep)
+{
+	CString ret;
+	POSITION pos = sl.GetHeadPosition();
+	while(pos)
+	{
+		ret += sl.GetNext(pos);
+		if(pos) ret += sep;
+	}
+	return(ret);
+}
+*/
+
+DWORD CharSetToCodePage(DWORD dwCharSet)
+{
+	if(dwCharSet == CP_UTF8) return CP_UTF8;
+	if(dwCharSet == CP_UTF7) return CP_UTF7;
+	CHARSETINFO cs={0};
+	::TranslateCharsetInfo((DWORD *)dwCharSet, &cs, TCI_SRCCHARSET);
+	return cs.ciACP;
+}
+
+CStringA ConvertMBCS(CStringA str, DWORD SrcCharSet, DWORD DstCharSet)
+{
+	WCHAR* utf16 = new WCHAR[str.GetLength()+1];
+	memset(utf16, 0, (str.GetLength()+1)*sizeof(WCHAR));
+
+	CHAR* mbcs = new CHAR[str.GetLength()*6+1];
+	memset(mbcs, 0, str.GetLength()*6+1);
+
+	int len = MultiByteToWideChar(
+		CharSetToCodePage(SrcCharSet), 0, 
+		str.GetBuffer(str.GetLength()), str.GetLength(), 
+		utf16, (str.GetLength()+1)*sizeof(WCHAR));
+
+	len = WideCharToMultiByte(
+		CharSetToCodePage(DstCharSet), 0, 
+		utf16, len, 
+		mbcs, str.GetLength()*6,
+		NULL, NULL);
+
+	str = mbcs;
+
+	delete [] utf16;
+	delete [] mbcs;
+
+	return str;
+}
+
+CStringA UrlEncode(CStringA str, bool fRaw)
+{
+	CStringA urlstr;
+
+	for(int i = 0; i < str.GetLength(); i++)
+	{
+		CHAR c = str[i];
+		if(fRaw && c == '+') urlstr += "%2B";
+		else if(c > 0x20 && c < 0x7f && c != '&') urlstr += c;
+		else if(c == 0x20) urlstr += fRaw ? ' ' : '+';
+		else {CStringA tmp; tmp.Format("%%%02x", (BYTE)c); urlstr += tmp;}
+	}
+
+	return urlstr;
+}
+
+CStringA UrlDecode(CStringA str, bool fRaw)
+{
+	str.Replace("&amp;", "&");
+
+	CHAR* s = str.GetBuffer(str.GetLength());
+	CHAR* e = s + str.GetLength();
+	CHAR* s1 = s;
+	CHAR* s2 = s;
+	while(s1 < e)
+	{
+		CHAR s11 = (s1 < e-1) ? (__isascii(s1[1]) && isupper(s1[1]) ? tolower(s1[1]) : s1[1]) : 0;
+		CHAR s12 = (s1 < e-2) ? (__isascii(s1[2]) && isupper(s1[2]) ? tolower(s1[2]) : s1[2]) : 0;
+
+		if(*s1 == '%' && s1 < e-2
+		&& (s1[1] >= '0' && s1[1] <= '9' || s11 >= 'a' && s11 <= 'f')
+		&& (s1[2] >= '0' && s1[2] <= '9' || s12 >= 'a' && s12 <= 'f'))
+		{
+			s1[1] = s11;
+			s1[2] = s12;
+			*s2 = 0;
+			if(s1[1] >= '0' && s1[1] <= '9') *s2 |= s1[1]-'0';
+			else if(s1[1] >= 'a' && s1[1] <= 'f') *s2 |= s1[1]-'a'+10;
+			*s2 <<= 4;
+			if(s1[2] >= '0' && s1[2] <= '9') *s2 |= s1[2]-'0';
+			else if(s1[2] >= 'a' && s1[2] <= 'f') *s2 |= s1[2]-'a'+10;
+			s1 += 2;
+		}
+		else 
+		{
+			*s2 = *s1 == '+' && !fRaw ? ' ' : *s1;
+		}
+
+		s1++;
+		s2++;
+	}
+
+	str.ReleaseBuffer(s2 - s);
+
+	return str;
+}
+
+CString ExtractTag(CString tag, CMapStringToString& attribs, bool& fClosing)
+{
+	tag.Trim();
+	attribs.RemoveAll();
+
+	fClosing = !tag.IsEmpty() ? tag[0] == '/' : false;
+	tag.TrimLeft('/');
+
+	int i = tag.Find(' ');
+	if(i < 0) i = tag.GetLength();
+	CString type = tag.Left(i).MakeLower();
+	tag = tag.Mid(i).Trim();
+
+	while((i = tag.Find('=')) > 0)
+	{
+		CString attrib = tag.Left(i).Trim().MakeLower();
+		tag = tag.Mid(i+1);
+		for(i = 0; i < tag.GetLength() && _istspace(tag[i]); i++);
+		tag = i < tag.GetLength() ? tag.Mid(i) : _T("");
+		if(!tag.IsEmpty() && tag[0] == '\"') {tag = tag.Mid(1); i = tag.Find('\"');}
+		else i = tag.Find(' ');
+		if(i < 0) i = tag.GetLength();
+		CString param = tag.Left(i).Trim();
+		if(!param.IsEmpty())
+			attribs[attrib] = param;
+		tag = i+1 < tag.GetLength() ? tag.Mid(i+1) : _T("");
+	}
+
+	return(type);
+}
+
+CAtlList<CString>& MakeLower(CAtlList<CString>& sl)
+{
+	POSITION pos = sl.GetHeadPosition();
+	while(pos) sl.GetNext(pos).MakeLower();
+	return sl;
+}
+
+CAtlList<CString>& MakeUpper(CAtlList<CString>& sl)
+{
+	POSITION pos = sl.GetHeadPosition();
+	while(pos) sl.GetNext(pos).MakeUpper();
+	return sl;
+}
+
+CAtlList<CString>& RemoveStrings(CAtlList<CString>& sl, int minlen, int maxlen)
+{
+	POSITION pos = sl.GetHeadPosition();
+	while(pos)
+	{
+		POSITION tmp = pos;
+		CString& str = sl.GetNext(pos);
+		int len = str.GetLength();
+		if(len < minlen || len > maxlen) sl.RemoveAt(tmp);
+	}
+	return sl;
+}
diff --git a/vsfilter/dsutil/text.h b/vsfilter/dsutil/text.h
new file mode 100644
index 0000000000000000000000000000000000000000..7c36627b17622e6c34080f3337d5a87c6fd65079
--- /dev/null
+++ b/vsfilter/dsutil/text.h
@@ -0,0 +1,69 @@
+#pragma once
+
+#include <atlcoll.h>
+
+// extern CString ExplodeMin(CString str, CAtlList<CString>& sl, TCHAR sep, int limit = 0);
+// extern CString Explode(CString str, CAtlList<CString>& sl, TCHAR sep, int limit = 0);
+// extern CString Implode(CAtlList<CString>& sl, TCHAR sep);
+
+template<class T, typename SEP>
+T Explode(T str, CAtlList<T>& sl, SEP sep, int limit = 0)
+{
+	sl.RemoveAll();
+
+	for(int i = 0, j = 0; ; i = j+1)
+	{
+		j = str.Find(sep, i);
+
+		if(j < 0 || sl.GetCount() == limit-1)
+		{
+			sl.AddTail(str.Mid(i).Trim());
+			break;
+		}
+		else
+		{
+			sl.AddTail(str.Mid(i, j-i).Trim());
+		}		
+	}
+
+	return sl.GetHead();
+}
+
+template<class T, typename SEP>
+T ExplodeMin(T str, CAtlList<T>& sl, SEP sep, int limit = 0)
+{
+	Explode(str, sl, sep, limit);
+	POSITION pos = sl.GetHeadPosition();
+	while(pos) 
+	{
+		POSITION tmp = pos;
+		if(sl.GetNext(pos).IsEmpty())
+			sl.RemoveAt(tmp);
+	}
+	if(sl.IsEmpty()) sl.AddTail(T()); // eh
+
+	return sl.GetHead();
+}
+
+template<class T, typename SEP>
+T Implode(CAtlList<T>& sl, SEP sep)
+{
+	T ret;
+	POSITION pos = sl.GetHeadPosition();
+	while(pos)
+	{
+		ret += sl.GetNext(pos);
+		if(pos) ret += sep;
+	}
+	return(ret);
+}
+
+extern CString ExtractTag(CString tag, CMapStringToString& attribs, bool& fClosing);
+extern CStringA ConvertMBCS(CStringA str, DWORD SrcCharSet, DWORD DstCharSet);
+extern CStringA UrlEncode(CStringA str, bool fRaw = false);
+extern CStringA UrlDecode(CStringA str, bool fRaw = false);
+extern DWORD CharSetToCodePage(DWORD dwCharSet);
+extern CAtlList<CString>& MakeLower(CAtlList<CString>& sl);
+extern CAtlList<CString>& MakeUpper(CAtlList<CString>& sl);
+extern CAtlList<CString>& RemoveStrings(CAtlList<CString>& sl, int minlen, int maxlen);
+
diff --git a/vsfilter/dsutil/vd.cpp b/vsfilter/dsutil/vd.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bb7dc8beaa968c0442e8fed331a48ca9c996c714
--- /dev/null
+++ b/vsfilter/dsutil/vd.cpp
@@ -0,0 +1,1509 @@
+//	VirtualDub - Video processing and capture application
+//	Copyright (C) 1998-2001 Avery Lee
+//
+//	This program is free software; you can redistribute it and/or modify
+//	it under the terms of the GNU General Public License as published by
+//	the Free Software Foundation; either version 2 of the License, or
+//	(at your option) any later version.
+//
+//	This program is distributed in the hope that it will be useful,
+//	but WITHOUT ANY WARRANTY; without even the implied warranty of
+//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//	GNU General Public License for more details.
+//
+//	You should have received a copy of the GNU General Public License
+//	along with this program; if not, write to the Free Software
+//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+//  Notes: 
+//  - BitBltFromI420ToRGB is from VirtualDub
+//	- The core assembly function of CCpuID is from DVD2AVI
+//  - sse2 yv12 to yuy2 conversion by Haali
+//	(- vd.cpp/h should be renamed to something more sensible already :)
+
+
+#include "stdafx.h"
+#include "vd.h"
+
+#pragma warning(disable : 4799) // no emms... blahblahblah
+
+CCpuID::CCpuID()
+{
+	DWORD flags = 0;
+
+	__asm
+	{
+		mov			eax, 1
+		cpuid
+		test		edx, 0x00800000		// STD MMX
+		jz			TEST_SSE
+		or			[flags], 1
+TEST_SSE:
+		test		edx, 0x02000000		// STD SSE
+		jz			TEST_SSE2
+		or			[flags], 2
+		or			[flags], 4
+TEST_SSE2:
+		test		edx, 0x04000000		// SSE2	
+		jz			TEST_3DNOW
+		or			[flags], 8
+TEST_3DNOW:
+		mov			eax, 0x80000001
+		cpuid
+		test		edx, 0x80000000		// 3D NOW
+		jz			TEST_SSEMMX
+		or			[flags], 16
+TEST_SSEMMX:
+		test		edx, 0x00400000		// SSE MMX
+		jz			TEST_END
+		or			[flags], 2
+TEST_END:
+	}
+
+	m_flags = (flag_t)flags;
+}
+
+CCpuID g_cpuid;
+
+void memcpy_accel(void* dst, const void* src, size_t len)
+{
+	if((g_cpuid.m_flags & CCpuID::flag_t::ssefpu) && len >= 128 
+		&& !((DWORD)src&15) && !((DWORD)dst&15))
+	{
+		__asm
+		{
+			mov     esi, dword ptr [src]
+			mov     edi, dword ptr [dst]
+			mov     ecx, len
+			shr     ecx, 7
+	memcpy_accel_sse_loop:
+			prefetchnta	[esi+16*8]
+			movaps		xmm0, [esi]
+			movaps		xmm1, [esi+16*1]
+			movaps		xmm2, [esi+16*2]
+			movaps		xmm3, [esi+16*3]
+			movaps		xmm4, [esi+16*4]
+			movaps		xmm5, [esi+16*5]
+			movaps		xmm6, [esi+16*6]
+			movaps		xmm7, [esi+16*7]
+			movntps		[edi], xmm0
+			movntps		[edi+16*1], xmm1
+			movntps		[edi+16*2], xmm2
+			movntps		[edi+16*3], xmm3
+			movntps		[edi+16*4], xmm4
+			movntps		[edi+16*5], xmm5
+			movntps		[edi+16*6], xmm6
+			movntps		[edi+16*7], xmm7
+			add			esi, 128
+			add			edi, 128
+			dec			ecx
+			jne			memcpy_accel_sse_loop
+			mov     ecx, len
+			and     ecx, 127
+			cmp     ecx, 0
+			je		memcpy_accel_sse_end
+	memcpy_accel_sse_loop2:
+			mov		dl, byte ptr[esi] 
+			mov		byte ptr[edi], dl
+			inc		esi
+			inc		edi
+			dec		ecx
+			jne		memcpy_accel_sse_loop2
+	memcpy_accel_sse_end:
+			emms
+			sfence
+		}
+	}
+	else if((g_cpuid.m_flags & CCpuID::flag_t::mmx) && len >= 64
+		&& !((DWORD)src&7) && !((DWORD)dst&7))
+	{
+		__asm 
+		{
+			mov     esi, dword ptr [src]
+			mov     edi, dword ptr [dst]
+			mov     ecx, len
+			shr     ecx, 6
+	memcpy_accel_mmx_loop:
+			movq    mm0, qword ptr [esi]
+			movq    mm1, qword ptr [esi+8*1]
+			movq    mm2, qword ptr [esi+8*2]
+			movq    mm3, qword ptr [esi+8*3]
+			movq    mm4, qword ptr [esi+8*4]
+			movq    mm5, qword ptr [esi+8*5]
+			movq    mm6, qword ptr [esi+8*6]
+			movq    mm7, qword ptr [esi+8*7]
+			movq    qword ptr [edi], mm0
+			movq    qword ptr [edi+8*1], mm1
+			movq    qword ptr [edi+8*2], mm2
+			movq    qword ptr [edi+8*3], mm3
+			movq    qword ptr [edi+8*4], mm4
+			movq    qword ptr [edi+8*5], mm5
+			movq    qword ptr [edi+8*6], mm6
+			movq    qword ptr [edi+8*7], mm7
+			add     esi, 64
+			add     edi, 64
+			loop	memcpy_accel_mmx_loop
+			mov     ecx, len
+			and     ecx, 63
+			cmp     ecx, 0
+			je		memcpy_accel_mmx_end
+	memcpy_accel_mmx_loop2:
+			mov		dl, byte ptr [esi] 
+			mov		byte ptr [edi], dl
+			inc		esi
+			inc		edi
+			dec		ecx
+			jne		memcpy_accel_mmx_loop2
+	memcpy_accel_mmx_end:
+			emms
+		}
+	}
+	else
+	{
+		memcpy(dst, src, len);
+	}
+}
+
+bool BitBltFromI420ToI420(int w, int h, BYTE* dsty, BYTE* dstu, BYTE* dstv, int dstpitch, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch)
+{
+	if((w&1)) return(false);
+
+	if(w > 0 && w == srcpitch && w == dstpitch)
+	{
+		memcpy_accel(dsty, srcy, h*srcpitch);
+		memcpy_accel(dstu, srcu, h/2*srcpitch/2);
+		memcpy_accel(dstv, srcv, h/2*srcpitch/2);
+	}
+	else
+	{
+		int pitch = min(abs(srcpitch), abs(dstpitch));
+
+		for(int y = 0; y < h; y++, srcy += srcpitch, dsty += dstpitch)
+			memcpy_accel(dsty, srcy, pitch);
+
+		srcpitch >>= 1;
+		dstpitch >>= 1;
+
+		pitch = min(abs(srcpitch), abs(dstpitch));
+
+		for(int y = 0; y < h; y+=2, srcu += srcpitch, dstu += dstpitch)
+			memcpy_accel(dstu, srcu, pitch);
+
+		for(int y = 0; y < h; y+=2, srcv += srcpitch, dstv += dstpitch)
+			memcpy_accel(dstv, srcv, pitch);
+	}
+
+	return true;
+}
+
+bool BitBltFromYUY2ToYUY2(int w, int h, BYTE* dst, int dstpitch, BYTE* src, int srcpitch)
+{
+	if(w > 0 && w == srcpitch && w == dstpitch)
+	{
+		memcpy_accel(dst, src, h*srcpitch);
+	}
+	else
+	{
+		int pitch = min(abs(srcpitch), abs(dstpitch));
+
+		for(int y = 0; y < h; y++, src += srcpitch, dst += dstpitch)
+			memcpy_accel(dst, src, pitch);
+	}
+
+	return(true);
+}
+
+extern "C" void asm_YUVtoRGB32_row(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB24_row(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB16_row(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB32_row_MMX(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB24_row_MMX(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB16_row_MMX(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB32_row_ISSE(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB24_row_ISSE(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+extern "C" void asm_YUVtoRGB16_row_ISSE(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width);
+
+bool BitBltFromI420ToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch)
+{
+	if(w<=0 || h<=0 || (w&1) || (h&1))
+		return(false);
+
+	void (*asm_YUVtoRGB_row)(void* ARGB1, void* ARGB2, BYTE* Y1, BYTE* Y2, BYTE* U, BYTE* V, long width) = NULL;;
+
+	if((g_cpuid.m_flags & CCpuID::ssefpu) && !(w&7))
+	{
+		switch(dbpp)
+		{
+		case 16: asm_YUVtoRGB_row = asm_YUVtoRGB16_row/*_ISSE*/; break; // TODO: fix _ISSE (555->565)
+		case 24: asm_YUVtoRGB_row = asm_YUVtoRGB24_row_ISSE; break;
+		case 32: asm_YUVtoRGB_row = asm_YUVtoRGB32_row_ISSE; break;
+		}
+	}
+	else if((g_cpuid.m_flags & CCpuID::mmx) && !(w&7))
+	{
+		switch(dbpp)
+		{
+		case 16: asm_YUVtoRGB_row = asm_YUVtoRGB16_row/*_MMX*/; break; // TODO: fix _MMX (555->565)
+		case 24: asm_YUVtoRGB_row = asm_YUVtoRGB24_row_MMX; break;
+		case 32: asm_YUVtoRGB_row = asm_YUVtoRGB32_row_MMX; break;
+		}
+	}
+	else
+	{
+		switch(dbpp)
+		{
+		case 16: asm_YUVtoRGB_row = asm_YUVtoRGB16_row; break;
+		case 24: asm_YUVtoRGB_row = asm_YUVtoRGB24_row; break;
+		case 32: asm_YUVtoRGB_row = asm_YUVtoRGB32_row; break;
+		}
+	}
+
+	if(!asm_YUVtoRGB_row) 
+		return(false);
+
+	do
+	{
+		asm_YUVtoRGB_row(dst + dstpitch, dst, srcy + srcpitch, srcy, srcu, srcv, w/2);
+
+		dst += 2*dstpitch;
+		srcy += srcpitch*2;
+		srcu += srcpitch/2;
+		srcv += srcpitch/2;
+	}
+	while(h -= 2);
+
+	if(g_cpuid.m_flags & CCpuID::mmx)
+		__asm emms
+
+	if(g_cpuid.m_flags & CCpuID::ssefpu)
+		__asm sfence
+
+	return true;
+}
+
+static void yuvtoyuy2row_c(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width)
+{
+	WORD* dstw = (WORD*)dst;
+	for(; width > 1; width -= 2)
+	{
+		*dstw++ = (*srcu++<<8)|*srcy++;
+		*dstw++ = (*srcv++<<8)|*srcy++;
+	}
+}
+
+static void __declspec(naked) yuvtoyuy2row_MMX(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width)
+{
+	__asm {
+		push	ebp
+		push	edi
+		push	esi
+		push	ebx
+
+		mov		edi, [esp+20] // dst
+		mov		ebp, [esp+24] // srcy
+		mov		ebx, [esp+28] // srcu
+		mov		esi, [esp+32] // srcv
+		mov		ecx, [esp+36] // width
+
+		shr		ecx, 3
+
+yuvtoyuy2row_loop:
+
+		movd		mm0, [ebx]
+		punpcklbw	mm0, [esi]
+
+		movq		mm1, [ebp]
+		movq		mm2, mm1
+		punpcklbw	mm1, mm0
+		punpckhbw	mm2, mm0
+
+		movq		[edi], mm1
+		movq		[edi+8], mm2
+
+		add		ebp, 8
+		add		ebx, 4
+		add		esi, 4
+        add		edi, 16
+
+		dec		ecx
+		jnz		yuvtoyuy2row_loop
+
+		pop		ebx
+		pop		esi
+		pop		edi
+		pop		ebp
+		ret
+	};
+}
+
+static void yuvtoyuy2row_avg_c(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width, DWORD pitchuv)
+{
+	WORD* dstw = (WORD*)dst;
+	for(; width > 1; width -= 2, srcu++, srcv++)
+	{
+		*dstw++ = (((srcu[0]+srcu[pitchuv])>>1)<<8)|*srcy++;
+		*dstw++ = (((srcv[0]+srcv[pitchuv])>>1)<<8)|*srcy++;
+	}
+}
+
+static void __declspec(naked) yuvtoyuy2row_avg_MMX(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width, DWORD pitchuv)
+{
+	static const __int64 mask = 0x7f7f7f7f7f7f7f7fi64;
+
+	__asm {
+		push	ebp
+		push	edi
+		push	esi
+		push	ebx
+
+		movq	mm7, mask
+
+		mov		edi, [esp+20] // dst
+		mov		ebp, [esp+24] // srcy
+		mov		ebx, [esp+28] // srcu
+		mov		esi, [esp+32] // srcv
+		mov		ecx, [esp+36] // width
+		mov		eax, [esp+40] // pitchuv
+
+		shr		ecx, 3
+
+yuvtoyuy2row_avg_loop:
+
+		movd		mm0, [ebx]
+		punpcklbw	mm0, [esi]
+		movq		mm1, mm0
+
+		movd		mm2, [ebx + eax]
+		punpcklbw	mm2, [esi + eax]
+		movq		mm3, mm2
+
+		// (x+y)>>1 == (x&y)+((x^y)>>1)
+
+		pand		mm0, mm2
+		pxor		mm1, mm3
+		psrlq		mm1, 1
+		pand		mm1, mm7
+		paddb		mm0, mm1
+
+		movq		mm1, [ebp]
+		movq		mm2, mm1
+		punpcklbw	mm1, mm0
+		punpckhbw	mm2, mm0
+
+		movq		[edi], mm1
+		movq		[edi+8], mm2
+
+		add		ebp, 8
+		add		ebx, 4
+		add		esi, 4
+        add		edi, 16
+
+		dec		ecx
+		jnz		yuvtoyuy2row_avg_loop
+
+		pop		ebx
+		pop		esi
+		pop		edi
+		pop		ebp
+		ret
+	};
+}
+
+static void __declspec(naked) yv12_yuy2_row_sse2() {
+  __asm {
+    // ebx - Y
+    // edx - U
+    // esi - V
+    // edi - dest
+    // ecx - halfwidth
+    xor     eax, eax
+
+one:
+    movdqa  xmm0, [ebx + eax*2]    // YYYYYYYY
+    movdqa  xmm1, [ebx + eax*2 + 16]    // YYYYYYYY
+
+    movdqa  xmm2, [edx + eax]      // UUUUUUUU
+    movdqa  xmm3, [esi + eax]      // VVVVVVVV
+
+    movdqa  xmm4, xmm2
+    movdqa  xmm5, xmm0
+    movdqa  xmm6, xmm1
+    punpcklbw xmm2, xmm3          // VUVUVUVU
+    punpckhbw xmm4, xmm3          // VUVUVUVU
+
+    punpcklbw xmm0, xmm2          // VYUYVYUY
+    punpcklbw xmm1, xmm4
+    punpckhbw xmm5, xmm2
+    punpckhbw xmm6, xmm4
+
+    movntdq [edi + eax*4], xmm0
+    movntdq [edi + eax*4 + 16], xmm5
+    movntdq [edi + eax*4 + 32], xmm1
+    movntdq [edi + eax*4 + 48], xmm6
+
+    add     eax, 16
+    cmp     eax, ecx
+
+    jb      one
+
+    ret
+  };
+}
+
+static void __declspec(naked) yv12_yuy2_row_sse2_linear() {
+  __asm {
+    // ebx - Y
+    // edx - U
+    // esi - V
+    // edi - dest
+    // ecx - width
+    // ebp - uv_stride
+    xor     eax, eax
+
+one:
+    movdqa  xmm0, [ebx + eax*2]    // YYYYYYYY
+    movdqa  xmm1, [ebx + eax*2 + 16]    // YYYYYYYY
+
+    movdqa  xmm2, [edx]
+    movdqa  xmm3, [esi]
+    pavgb   xmm2, [edx + ebp]      // UUUUUUUU
+    pavgb   xmm3, [esi + ebp]      // VVVVVVVV
+
+    movdqa  xmm4, xmm2
+    movdqa  xmm5, xmm0
+    movdqa  xmm6, xmm1
+    punpcklbw xmm2, xmm3          // VUVUVUVU
+    punpckhbw xmm4, xmm3          // VUVUVUVU
+
+    punpcklbw xmm0, xmm2          // VYUYVYUY
+    punpcklbw xmm1, xmm4
+    punpckhbw xmm5, xmm2
+    punpckhbw xmm6, xmm4
+
+    movntdq [edi + eax*4], xmm0
+    movntdq [edi + eax*4 + 16], xmm5
+    movntdq [edi + eax*4 + 32], xmm1
+    movntdq [edi + eax*4 + 48], xmm6
+
+    add     eax, 16
+    add     edx, 16
+    add     esi, 16
+    cmp     eax, ecx
+
+    jb      one
+
+    ret
+  };
+}
+
+static void __declspec(naked) yv12_yuy2_row_sse2_linear_interlaced() {
+  __asm {
+    // ebx - Y
+    // edx - U
+    // esi - V
+    // edi - dest
+    // ecx - width
+    // ebp - uv_stride
+    xor     eax, eax
+
+one:
+    movdqa  xmm0, [ebx + eax*2]    // YYYYYYYY
+    movdqa  xmm1, [ebx + eax*2 + 16]    // YYYYYYYY
+
+    movdqa  xmm2, [edx]
+    movdqa  xmm3, [esi]
+    pavgb   xmm2, [edx + ebp*2]      // UUUUUUUU
+    pavgb   xmm3, [esi + ebp*2]      // VVVVVVVV
+
+    movdqa  xmm4, xmm2
+    movdqa  xmm5, xmm0
+    movdqa  xmm6, xmm1
+    punpcklbw xmm2, xmm3          // VUVUVUVU
+    punpckhbw xmm4, xmm3          // VUVUVUVU
+
+    punpcklbw xmm0, xmm2          // VYUYVYUY
+    punpcklbw xmm1, xmm4
+    punpckhbw xmm5, xmm2
+    punpckhbw xmm6, xmm4
+
+    movntdq [edi + eax*4], xmm0
+    movntdq [edi + eax*4 + 16], xmm5
+    movntdq [edi + eax*4 + 32], xmm1
+    movntdq [edi + eax*4 + 48], xmm6
+
+    add     eax, 16
+    add     edx, 16
+    add     esi, 16
+    cmp     eax, ecx
+
+    jb      one
+
+    ret
+  };
+}
+
+void __declspec(naked) yv12_yuy2_sse2(const BYTE *Y, const BYTE *U, const BYTE *V,
+    int halfstride, unsigned halfwidth, unsigned height,
+    BYTE *YUY2, int d_stride)
+{
+  __asm {
+    push    ebx
+    push    esi
+    push    edi
+    push    ebp
+
+    mov     ebx, [esp + 20] // Y
+    mov     edx, [esp + 24] // U
+    mov     esi, [esp + 28] // V
+    mov     edi, [esp + 44] // D
+    mov     ebp, [esp + 32] // uv_stride
+    mov     ecx, [esp + 36] // uv_width
+
+    mov     eax, ecx
+    add     eax, 15
+    and     eax, 0xfffffff0
+    sub     [esp + 32], eax
+
+    cmp     dword ptr [esp + 40], 2
+    jbe     last2
+
+row:
+    sub     dword ptr [esp + 40], 2
+    call    yv12_yuy2_row_sse2
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    call    yv12_yuy2_row_sse2_linear
+
+    add     edx, [esp + 32]
+    add     esi, [esp + 32]
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    cmp     dword ptr [esp + 40], 2
+    ja      row
+
+last2:
+    call    yv12_yuy2_row_sse2
+
+    dec     dword ptr [esp + 40]
+    jz      done
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+    call    yv12_yuy2_row_sse2
+done:
+
+    pop     ebp
+    pop     edi
+    pop     esi
+    pop     ebx
+
+    ret
+  };
+}
+
+void __declspec(naked) yv12_yuy2_sse2_interlaced(const BYTE *Y, const BYTE *U, const BYTE *V,
+    int halfstride, unsigned halfwidth, unsigned height,
+    BYTE *YUY2, int d_stride)
+{
+  __asm {
+    push    ebx
+    push    esi
+    push    edi
+    push    ebp
+
+    mov     ebx, [esp + 20] // Y
+    mov     edx, [esp + 24] // U
+    mov     esi, [esp + 28] // V
+    mov     edi, [esp + 44] // D
+    mov     ebp, [esp + 32] // uv_stride
+    mov     ecx, [esp + 36] // uv_width
+
+    mov     eax, ecx
+    add     eax, 15
+    and     eax, 0xfffffff0
+    sub     [esp + 32], eax
+
+    cmp     dword ptr [esp + 40], 4
+    jbe     last4
+
+row:
+    sub     dword ptr [esp + 40], 4
+    call    yv12_yuy2_row_sse2	// first row, first field
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    add	    edx, ebp
+    add	    esi, ebp
+
+    call    yv12_yuy2_row_sse2	// first row, second field
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    sub	    edx, ebp
+    sub	    esi, ebp
+
+    call    yv12_yuy2_row_sse2_linear_interlaced // second row, first field
+
+    add     edx, [esp + 32]
+    add     esi, [esp + 32]
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    call    yv12_yuy2_row_sse2_linear_interlaced // second row, second field
+
+    add     edx, [esp + 32]
+    add     esi, [esp + 32]
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    cmp     dword ptr [esp + 40], 4
+    ja      row
+
+last4:
+    call    yv12_yuy2_row_sse2
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    add     edx, ebp
+    add     esi, ebp
+
+    call    yv12_yuy2_row_sse2
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    sub     edx, ebp
+    sub     esi, ebp
+
+    call    yv12_yuy2_row_sse2
+
+    lea     ebx, [ebx + ebp*2]
+    add     edi, [esp + 48]
+
+    add     edx, ebp
+    add     esi, ebp
+
+    call    yv12_yuy2_row_sse2
+
+    pop     ebp
+    pop     edi
+    pop     esi
+    pop     ebx
+
+    ret
+  };
+}
+
+bool BitBltFromI420ToYUY2(int w, int h, BYTE* dst, int dstpitch, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch, bool fInterlaced)
+{
+	if(w<=0 || h<=0 || (w&1) || (h&1))
+		return(false);
+
+	if(srcpitch == 0) srcpitch = w;
+
+	if((g_cpuid.m_flags & CCpuID::sse2) 
+		&& ((DWORD_PTR)srcy&15) && ((DWORD_PTR)srcu&15) && ((DWORD_PTR)srcv&15) && !(srcpitch&15) 
+		&& ((DWORD_PTR)dst&15) && !(dstpitch&15))
+	{
+		if(!fInterlaced) yv12_yuy2_sse2(srcy, srcu, srcv, srcpitch/2, w/2, h, dst, dstpitch);
+		else yv12_yuy2_sse2_interlaced(srcy, srcu, srcv, srcpitch/2, w/2, h, dst, dstpitch);
+		return true;
+	}
+	else
+	{
+		ASSERT(!fInterlaced);
+	}
+
+	void (*yuvtoyuy2row)(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width) = NULL;
+	void (*yuvtoyuy2row_avg)(BYTE* dst, BYTE* srcy, BYTE* srcu, BYTE* srcv, DWORD width, DWORD pitchuv) = NULL;
+
+	if((g_cpuid.m_flags & CCpuID::mmx) && !(w&7))
+	{
+		yuvtoyuy2row = yuvtoyuy2row_MMX;
+		yuvtoyuy2row_avg = yuvtoyuy2row_avg_MMX;
+	}
+	else
+	{
+		yuvtoyuy2row = yuvtoyuy2row_c;
+		yuvtoyuy2row_avg = yuvtoyuy2row_avg_c;
+	}
+
+	if(!yuvtoyuy2row) 
+		return(false);
+
+	do
+	{
+		yuvtoyuy2row(dst, srcy, srcu, srcv, w);
+		yuvtoyuy2row_avg(dst + dstpitch, srcy + srcpitch, srcu, srcv, w, srcpitch/2);
+
+		dst += 2*dstpitch;
+		srcy += srcpitch*2;
+		srcu += srcpitch/2;
+		srcv += srcpitch/2;
+	}
+	while((h -= 2) > 2);
+
+	yuvtoyuy2row(dst, srcy, srcu, srcv, w);
+	yuvtoyuy2row(dst + dstpitch, srcy + srcpitch, srcu, srcv, w);
+
+	if(g_cpuid.m_flags & CCpuID::mmx)
+		__asm emms
+
+	return(true);
+}
+
+bool BitBltFromRGBToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* src, int srcpitch, int sbpp)
+{
+	if(dbpp == sbpp)
+	{
+		int rowbytes = w*dbpp>>3;
+
+		if(rowbytes > 0 && rowbytes == srcpitch && rowbytes == dstpitch)
+		{
+			memcpy_accel(dst, src, h*rowbytes);
+		}
+		else
+		{
+			for(int y = 0; y < h; y++, src += srcpitch, dst += dstpitch)
+				memcpy_accel(dst, src, rowbytes);
+		}
+
+		return(true);
+	}
+	
+	if(sbpp != 16 && sbpp != 24 && sbpp != 32
+	|| dbpp != 16 && dbpp != 24 && dbpp != 32)
+		return(false);
+
+	if(dbpp == 16)
+	{
+		for(int y = 0; y < h; y++, src += srcpitch, dst += dstpitch)
+		{
+			if(sbpp == 24)
+			{
+				BYTE* s = (BYTE*)src;
+				WORD* d = (WORD*)dst;
+				for(int x = 0; x < w; x++, s+=3, d++)
+					*d = (WORD)(((*((DWORD*)s)>>8)&0xf800)|((*((DWORD*)s)>>5)&0x07e0)|((*((DWORD*)s)>>3)&0x1f));
+			}
+			else if(sbpp == 32)
+			{
+				DWORD* s = (DWORD*)src;
+				WORD* d = (WORD*)dst;
+				for(int x = 0; x < w; x++, s++, d++)
+					*d = (WORD)(((*s>>8)&0xf800)|((*s>>5)&0x07e0)|((*s>>3)&0x1f));
+			}
+		}
+	}
+	else if(dbpp == 24)
+	{
+		for(int y = 0; y < h; y++, src += srcpitch, dst += dstpitch)
+		{
+			if(sbpp == 16)
+			{
+				WORD* s = (WORD*)src;
+				BYTE* d = (BYTE*)dst;
+				for(int x = 0; x < w; x++, s++, d+=3)
+				{	// not tested, r-g-b might be in reverse
+					d[0] = (*s&0x001f)<<3;
+					d[1] = (*s&0x07e0)<<5;
+					d[2] = (*s&0xf800)<<8;
+				}
+			}
+			else if(sbpp == 32)
+			{
+				BYTE* s = (BYTE*)src;
+				BYTE* d = (BYTE*)dst;
+				for(int x = 0; x < w; x++, s+=4, d+=3)
+					{d[0] = s[0]; d[1] = s[1]; d[2] = s[2];}
+			}
+		}
+	}
+	else if(dbpp == 32)
+	{
+		for(int y = 0; y < h; y++, src += srcpitch, dst += dstpitch)
+		{
+			if(sbpp == 16)
+			{
+				WORD* s = (WORD*)src;
+				DWORD* d = (DWORD*)dst;
+				for(int x = 0; x < w; x++, s++, d++)
+					*d = ((*s&0xf800)<<8)|((*s&0x07e0)<<5)|((*s&0x001f)<<3);
+			}
+			else if(sbpp == 24)
+			{	
+				BYTE* s = (BYTE*)src;
+				DWORD* d = (DWORD*)dst;
+				for(int x = 0; x < w; x++, s+=3, d++)
+					*d = *((DWORD*)s)&0xffffff;
+			}
+		}
+	}
+
+	return(true);
+}
+
+static void asm_blend_row_clipped_c(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	BYTE* src2 = src + srcpitch;
+	do {*dst++ = (*src++ + *src2++ + 1) >> 1;}
+	while(w--);
+}
+
+static void asm_blend_row_c(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	BYTE* src2 = src + srcpitch;
+	BYTE* src3 = src2 + srcpitch;
+	do {*dst++ = (*src++ + (*src2++ << 1) + *src3++ + 2) >> 2;}
+	while(w--);
+}
+
+static void __declspec(naked) asm_blend_row_clipped_MMX(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	static const __int64 _x0001000100010001 = 0x0001000100010001;
+
+	__asm {
+		push	ebp
+		push	edi
+		push	esi
+		push	ebx
+
+		mov		edi,[esp+20]
+		mov		esi,[esp+24]
+		sub		edi,esi
+		mov		ebp,[esp+28]
+		mov		edx,[esp+32]
+
+		shr		ebp, 3
+
+		movq	mm6, _x0001000100010001
+		pxor	mm7, mm7
+
+xloop:
+		movq		mm0, [esi]
+		movq		mm3, mm0
+		punpcklbw	mm0, mm7
+		punpckhbw	mm3, mm7
+
+		movq		mm1, [esi+edx]
+		movq		mm4, mm1
+		punpcklbw	mm1, mm7
+		punpckhbw	mm4, mm7
+
+		paddw		mm1, mm0
+		paddw		mm1, mm6
+		psrlw		mm1, 1
+
+		paddw		mm4, mm3
+		paddw		mm4, mm6
+		psrlw		mm4, 1
+
+		add			esi, 8
+		packuswb	mm1, mm4
+		movq		[edi+esi-8], mm1
+
+		dec		ebp
+		jne		xloop
+
+		pop		ebx
+		pop		esi
+		pop		edi
+		pop		ebp
+		ret
+	};
+}
+
+static void __declspec(naked) asm_blend_row_MMX(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	static const __int64 mask0 = 0xfcfcfcfcfcfcfcfci64;
+	static const __int64 mask1 = 0x7f7f7f7f7f7f7f7fi64;
+	static const __int64 mask2 = 0x3f3f3f3f3f3f3f3fi64;
+	static const __int64 _x0002000200020002 = 0x0002000200020002;
+
+	__asm {
+		push	ebp
+		push	edi
+		push	esi
+		push	ebx
+
+		mov		edi, [esp+20]
+		mov		esi, [esp+24]
+		sub		edi, esi
+		mov		ebp, [esp+28]
+		mov		edx, [esp+32]
+
+		shr		ebp, 3
+
+		movq	mm6, _x0002000200020002
+		pxor	mm7, mm7
+
+xloop:
+		movq		mm0, [esi]
+		movq		mm3, mm0
+		punpcklbw	mm0, mm7
+		punpckhbw	mm3, mm7
+
+		movq		mm1, [esi+edx]
+		movq		mm4, mm1
+		punpcklbw	mm1, mm7
+		punpckhbw	mm4, mm7
+
+		movq		mm2, [esi+edx*2]
+		movq		mm5, mm2
+		punpcklbw	mm2, mm7
+		punpckhbw	mm5, mm7
+
+		psllw		mm1, 1
+		paddw		mm1, mm0
+		paddw		mm1, mm2
+		paddw		mm1, mm6
+		psrlw		mm1, 2
+
+		psllw		mm4, 1
+		paddw		mm4, mm3
+		paddw		mm4, mm5
+		paddw		mm4, mm6
+		psrlw		mm4, 2
+
+		add			esi, 8
+		packuswb	mm1, mm4
+		movq		[edi+esi-8], mm1
+
+		dec		ebp
+		jne		xloop
+
+		// sadly the original code makes a lot of visible banding artifacts on yuv
+		// (it seems those shiftings without rounding introduce too much error)
+/*
+		mov		edi,[esp+20]
+		mov		esi,[esp+24]
+		sub		edi,esi
+		mov		ebp,[esp+28]
+		mov		edx,[esp+32]
+
+		movq	mm5,mask0
+		movq	mm6,mask1
+		movq	mm7,mask2
+		shr		ebp,1
+		jz		oddpart
+
+xloop:
+		movq	mm2,[esi]
+		movq	mm0,mm5
+
+		movq	mm1,[esi+edx]
+		pand	mm0,mm2
+
+		psrlq	mm1,1
+		movq	mm2,[esi+edx*2]
+
+		psrlq	mm2,2
+		pand	mm1,mm6
+
+		psrlq	mm0,2
+		pand	mm2,mm7
+
+		paddb	mm0,mm1
+		add		esi,8
+
+		paddb	mm0,mm2
+		dec		ebp
+
+		movq	[edi+esi-8],mm0
+		jne		xloop
+
+oddpart:
+		test	byte ptr [esp+28],1
+		jz		nooddpart
+
+		mov		ecx,[esi]
+		mov		eax,0fcfcfcfch
+		mov		ebx,[esi+edx]
+		and		eax,ecx
+		shr		ebx,1
+		mov		ecx,[esi+edx*2]
+		shr		ecx,2
+		and		ebx,07f7f7f7fh
+		shr		eax,2
+		and		ecx,03f3f3f3fh
+		add		eax,ebx
+		add		eax,ecx
+		mov		[edi+esi],eax
+
+nooddpart:
+*/
+		pop		ebx
+		pop		esi
+		pop		edi
+		pop		ebp
+		ret
+	};
+}
+
+__declspec(align(16)) static BYTE const_1_16_bytes[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+
+static void asm_blend_row_SSE2(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	__asm
+	{
+		mov edx, srcpitch
+		mov esi, src
+		mov edi, dst
+		sub edi, esi
+		mov ecx, w
+		mov ebx, ecx
+		shr ecx, 4
+		and ebx, 15
+
+		movdqa xmm7, [const_1_16_bytes] 
+
+asm_blend_row_SSE2_loop:
+		movdqa xmm0, [esi]
+		movdqa xmm1, [esi+edx]
+		movdqa xmm2, [esi+edx*2]
+		pavgb xmm0, xmm1
+		pavgb xmm2, xmm1
+		psubusb xmm0, xmm7
+		pavgb xmm0, xmm2
+		movdqa [esi+edi], xmm0
+		add esi, 16
+		dec	ecx
+		jnz asm_blend_row_SSE2_loop
+
+		test ebx,15
+		jz asm_blend_row_SSE2_end
+
+		mov ecx, ebx
+		xor ax, ax
+		xor bx, bx
+		xor dx, dx
+asm_blend_row_SSE2_loop2:
+		mov al, [esi]
+		mov bl, [esi+edx]
+		mov dl, [esi+edx*2]
+		add ax, bx
+		inc ax
+		shr ax, 1
+		add dx, bx
+		inc dx
+		shr dx, 1
+		add ax, dx
+		shr ax, 1
+		mov [esi+edi], al
+		inc esi
+		dec	ecx
+		jnz asm_blend_row_SSE2_loop2
+
+asm_blend_row_SSE2_end:
+	}
+}
+
+static void asm_blend_row_clipped_SSE2(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch)
+{
+	__asm
+	{
+		mov edx, srcpitch
+		mov esi, src
+		mov edi, dst
+		sub edi, esi
+		mov ecx, w
+		mov ebx, ecx
+		shr ecx, 4
+		and ebx, 15
+
+		movdqa xmm7, [const_1_16_bytes] 
+
+asm_blend_row_clipped_SSE2_loop:
+		movdqa xmm0, [esi]
+		movdqa xmm1, [esi+edx]
+		pavgb xmm0, xmm1
+		movdqa [esi+edi], xmm0
+		add esi, 16
+		dec	ecx
+		jnz asm_blend_row_clipped_SSE2_loop
+
+		test ebx,15
+		jz asm_blend_row_clipped_SSE2_end
+
+		mov ecx, ebx
+		xor ax, ax
+		xor bx, bx
+asm_blend_row_clipped_SSE2_loop2:
+		mov al, [esi]
+		mov bl, [esi+edx]
+		add ax, bx
+		inc ax
+		shr ax, 1
+		mov [esi+edi], al
+		inc esi
+		dec	ecx
+		jnz asm_blend_row_clipped_SSE2_loop2
+
+asm_blend_row_clipped_SSE2_end:
+	}
+}
+
+void DeinterlaceBlend(BYTE* dst, BYTE* src, DWORD rowbytes, DWORD h, DWORD dstpitch, DWORD srcpitch)
+{
+	void (*asm_blend_row_clipped)(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch) = NULL;
+	void (*asm_blend_row)(BYTE* dst, BYTE* src, DWORD w, DWORD srcpitch) = NULL;
+
+	if((g_cpuid.m_flags & CCpuID::sse2) && !((DWORD)src&0xf) && !((DWORD)dst&0xf) && !(srcpitch&0xf))
+	{
+		asm_blend_row_clipped = asm_blend_row_clipped_SSE2;
+		asm_blend_row = asm_blend_row_SSE2;
+	}
+	else if(g_cpuid.m_flags & CCpuID::mmx)
+	{
+		asm_blend_row_clipped = asm_blend_row_clipped_MMX;
+		asm_blend_row = asm_blend_row_MMX;
+	}
+	else
+	{
+		asm_blend_row_clipped = asm_blend_row_clipped_c;
+		asm_blend_row = asm_blend_row_c;
+	}
+
+	if(!asm_blend_row_clipped)
+		return;
+
+	asm_blend_row_clipped(dst, src, rowbytes, srcpitch);
+
+	if((h -= 2) > 0) do
+	{
+		dst += dstpitch;
+		asm_blend_row(dst, src, rowbytes, srcpitch);
+        src += srcpitch;
+	}
+	while(--h);
+
+	asm_blend_row_clipped(dst + dstpitch, src, rowbytes, srcpitch);
+
+	if(g_cpuid.m_flags & CCpuID::mmx)
+		__asm emms
+}
+
+void DeinterlaceBob(BYTE* dst, BYTE* src, DWORD rowbytes, DWORD h, DWORD dstpitch, DWORD srcpitch, bool topfield)
+{
+	if(topfield)
+	{
+		BitBltFromRGBToRGB(rowbytes, h/2, dst, dstpitch*2, 8, src, srcpitch*2, 8);
+		AvgLines8(dst, h, dstpitch);
+	}
+	else
+	{
+		BitBltFromRGBToRGB(rowbytes, h/2, dst + dstpitch, dstpitch*2, 8, src + srcpitch, srcpitch*2, 8);
+		AvgLines8(dst + dstpitch, h-1, dstpitch);
+	}
+}
+
+void AvgLines8(BYTE* dst, DWORD h, DWORD pitch)
+{
+	if(h <= 1) return;
+
+	BYTE* s = dst;
+	BYTE* d = dst + (h-2)*pitch;
+
+	for(; s < d; s += pitch*2)
+	{
+		BYTE* tmp = s;
+
+		if((g_cpuid.m_flags & CCpuID::sse2) && !((DWORD)tmp&0xf) && !((DWORD)pitch&0xf))
+		{
+			__asm
+			{
+				mov		esi, tmp
+				mov		ebx, pitch
+
+				mov		ecx, ebx
+				shr		ecx, 4
+
+AvgLines8_sse2_loop:
+				movdqa	xmm0, [esi]
+				pavgb	xmm0, [esi+ebx*2]
+				movdqa	[esi+ebx], xmm0
+				add		esi, 16
+
+				dec		ecx
+				jnz		AvgLines8_sse2_loop
+
+				mov		tmp, esi
+			}
+
+			for(int i = pitch&7; i--; tmp++)
+			{
+				tmp[pitch] = (tmp[0] + tmp[pitch<<1] + 1) >> 1;
+			}
+		}
+		else if(g_cpuid.m_flags & CCpuID::mmx)
+		{
+			__asm
+			{
+				mov		esi, tmp
+				mov		ebx, pitch
+
+				mov		ecx, ebx
+				shr		ecx, 3
+
+				pxor	mm7, mm7
+AvgLines8_mmx_loop:
+				movq	mm0, [esi]
+				movq	mm1, mm0
+
+				punpcklbw	mm0, mm7
+				punpckhbw	mm1, mm7
+
+				movq	mm2, [esi+ebx*2]
+				movq	mm3, mm2
+
+				punpcklbw	mm2, mm7
+				punpckhbw	mm3, mm7
+
+				paddw	mm0, mm2
+				psrlw	mm0, 1
+
+				paddw	mm1, mm3
+				psrlw	mm1, 1
+
+				packuswb	mm0, mm1
+
+				movq	[esi+ebx], mm0
+
+				lea		esi, [esi+8]
+
+				dec		ecx
+				jnz		AvgLines8_mmx_loop
+
+				mov		tmp, esi
+			}
+
+			for(int i = pitch&7; i--; tmp++)
+			{
+				tmp[pitch] = (tmp[0] + tmp[pitch<<1] + 1) >> 1;
+			}
+		}
+		else
+		{
+			for(int i = pitch; i--; tmp++)
+			{
+				tmp[pitch] = (tmp[0] + tmp[pitch<<1] + 1) >> 1;
+			}
+		}
+	}
+
+	if(!(h&1) && h >= 2)
+	{
+		dst += (h-2)*pitch;
+		memcpy_accel(dst + pitch, dst, pitch);
+	}
+
+	__asm emms;
+}
+
+void AvgLines555(BYTE* dst, DWORD h, DWORD pitch)
+{
+	if(h <= 1) return;
+
+	unsigned __int64 __0x7c007c007c007c00 = 0x7c007c007c007c00;
+	unsigned __int64 __0x03e003e003e003e0 = 0x03e003e003e003e0;
+	unsigned __int64 __0x001f001f001f001f = 0x001f001f001f001f;
+
+	BYTE* s = dst;
+	BYTE* d = dst + (h-2)*pitch;
+
+	for(; s < d; s += pitch*2)
+	{
+		BYTE* tmp = s;
+
+		__asm
+		{
+			mov		esi, tmp
+			mov		ebx, pitch
+
+			mov		ecx, ebx
+			shr		ecx, 3
+
+			movq	mm6, __0x03e003e003e003e0
+			movq	mm7, __0x001f001f001f001f
+
+AvgLines555_loop:
+			movq	mm0, [esi]
+			movq	mm1, mm0
+			movq	mm2, mm0
+
+			psrlw	mm0, 10				// red1 bits: mm0 = 001f001f001f001f
+			pand	mm1, mm6			// green1 bits: mm1 = 03e003e003e003e0
+			pand	mm2, mm7			// blue1 bits: mm2 = 001f001f001f001f
+
+			movq	mm3, [esi+ebx*2]
+			movq	mm4, mm3
+			movq	mm5, mm3
+
+			psrlw	mm3, 10				// red2 bits: mm3 = 001f001f001f001f
+			pand	mm4, mm6			// green2 bits: mm4 = 03e003e003e003e0
+			pand	mm5, mm7			// blue2 bits: mm5 = 001f001f001f001f
+
+			paddw	mm0, mm3
+			psrlw	mm0, 1				// (red1+red2)/2
+			psllw	mm0, 10				// red bits at 7c007c007c007c00
+
+			paddw	mm1, mm4
+			psrlw	mm1, 1				// (green1+green2)/2
+			pand	mm1, mm6			// green bits at 03e003e003e003e0
+
+			paddw	mm2, mm5
+			psrlw	mm2, 1				// (blue1+blue2)/2
+										// blue bits at 001f001f001f001f (no need to pand, lower bits were discareded)
+
+			por		mm0, mm1
+			por		mm0, mm2
+
+			movq	[esi+ebx], mm0
+
+			lea		esi, [esi+8]
+
+			dec		ecx
+			jnz		AvgLines555_loop
+
+			mov		tmp, esi
+		}
+
+		for(int i = (pitch&7)>>1; i--; tmp++)
+		{
+			tmp[pitch] = 
+				((((*tmp&0x7c00) + (tmp[pitch<<1]&0x7c00)) >> 1)&0x7c00)|
+				((((*tmp&0x03e0) + (tmp[pitch<<1]&0x03e0)) >> 1)&0x03e0)|
+				((((*tmp&0x001f) + (tmp[pitch<<1]&0x001f)) >> 1)&0x001f);
+		}
+	}
+
+	if(!(h&1) && h >= 2)
+	{
+		dst += (h-2)*pitch;
+		memcpy_accel(dst + pitch, dst, pitch);
+	}
+
+	__asm emms;
+}
+
+void AvgLines565(BYTE* dst, DWORD h, DWORD pitch)
+{
+	if(h <= 1) return;
+
+	unsigned __int64 __0xf800f800f800f800 = 0xf800f800f800f800;
+	unsigned __int64 __0x07e007e007e007e0 = 0x07e007e007e007e0;
+	unsigned __int64 __0x001f001f001f001f = 0x001f001f001f001f;
+
+	BYTE* s = dst;
+	BYTE* d = dst + (h-2)*pitch;
+
+	for(; s < d; s += pitch*2)
+	{
+		WORD* tmp = (WORD*)s;
+
+		__asm
+		{
+			mov		esi, tmp
+			mov		ebx, pitch
+
+			mov		ecx, ebx
+			shr		ecx, 3
+
+			movq	mm6, __0x07e007e007e007e0
+			movq	mm7, __0x001f001f001f001f
+
+AvgLines565_loop:
+			movq	mm0, [esi]
+			movq	mm1, mm0
+			movq	mm2, mm0
+
+			psrlw	mm0, 11				// red1 bits: mm0 = 001f001f001f001f
+			pand	mm1, mm6			// green1 bits: mm1 = 07e007e007e007e0
+			pand	mm2, mm7			// blue1 bits: mm2 = 001f001f001f001f
+
+			movq	mm3, [esi+ebx*2]
+			movq	mm4, mm3
+			movq	mm5, mm3
+
+			psrlw	mm3, 11				// red2 bits: mm3 = 001f001f001f001f
+			pand	mm4, mm6			// green2 bits: mm4 = 07e007e007e007e0
+			pand	mm5, mm7			// blue2 bits: mm5 = 001f001f001f001f
+
+			paddw	mm0, mm3
+			psrlw	mm0, 1				// (red1+red2)/2
+			psllw	mm0, 11				// red bits at f800f800f800f800
+
+			paddw	mm1, mm4
+			psrlw	mm1, 1				// (green1+green2)/2
+			pand	mm1, mm6			// green bits at 03e003e003e003e0
+
+			paddw	mm2, mm5
+			psrlw	mm2, 1				// (blue1+blue2)/2
+										// blue bits at 001f001f001f001f (no need to pand, lower bits were discareded)
+
+			por		mm0, mm1
+			por		mm0, mm2
+
+			movq	[esi+ebx], mm0
+
+			lea		esi, [esi+8]
+
+			dec		ecx
+			jnz		AvgLines565_loop
+
+			mov		tmp, esi
+		}
+
+		for(int i = (pitch&7)>>1; i--; tmp++)
+		{
+			tmp[pitch] = 
+				((((*tmp&0xf800) + (tmp[pitch<<1]&0xf800)) >> 1)&0xf800)|
+				((((*tmp&0x07e0) + (tmp[pitch<<1]&0x07e0)) >> 1)&0x07e0)|
+				((((*tmp&0x001f) + (tmp[pitch<<1]&0x001f)) >> 1)&0x001f);
+		}
+	}
+
+	if(!(h&1) && h >= 2)
+	{
+		dst += (h-2)*pitch;
+		memcpy_accel(dst + pitch, dst, pitch);
+	}
+
+	__asm emms;
+}
+
+extern "C" void mmx_YUY2toRGB24(const BYTE* src, BYTE* dst, const BYTE* src_end, int src_pitch, int row_size, bool rec709);
+extern "C" void mmx_YUY2toRGB32(const BYTE* src, BYTE* dst, const BYTE* src_end, int src_pitch, int row_size, bool rec709);
+
+bool BitBltFromYUY2ToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* src, int srcpitch)
+{
+	void (* YUY2toRGB)(const BYTE* src, BYTE* dst, const BYTE* src_end, int src_pitch, int row_size, bool rec709) = NULL;
+
+	if(g_cpuid.m_flags & CCpuID::mmx)
+	{
+		YUY2toRGB = 
+			dbpp == 32 ? mmx_YUY2toRGB32 :
+			dbpp == 24 ? mmx_YUY2toRGB24 :
+			// dbpp == 16 ? mmx_YUY2toRGB16 : // TODO
+			NULL;
+	}
+	else
+	{
+		// TODO
+	}
+
+	if(!YUY2toRGB) return(false);
+
+	YUY2toRGB(src, dst, src + h*srcpitch, srcpitch, w, false);
+
+	return(true);
+}
diff --git a/vsfilter/dsutil/vd.h b/vsfilter/dsutil/vd.h
new file mode 100644
index 0000000000000000000000000000000000000000..33f02fa99d097db5ff24b4484257ac3cc9365545
--- /dev/null
+++ b/vsfilter/dsutil/vd.h
@@ -0,0 +1,42 @@
+//	VirtualDub - Video processing and capture application
+//	Copyright (C) 1998-2001 Avery Lee
+//
+//	This program is free software; you can redistribute it and/or modify
+//	it under the terms of the GNU General Public License as published by
+//	the Free Software Foundation; either version 2 of the License, or
+//	(at your option) any later version.
+//
+//	This program is distributed in the hope that it will be useful,
+//	but WITHOUT ANY WARRANTY; without even the implied warranty of
+//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//	GNU General Public License for more details.
+//
+//	You should have received a copy of the GNU General Public License
+//	along with this program; if not, write to the Free Software
+//	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+//  Notes: 
+//  - BitBltFromI420ToRGB is from VirtualDub
+//  - BitBltFromYUY2ToRGB is from AviSynth 2.52
+//	- The core assembly function of CCpuID is from DVD2AVI
+//	(- vd.cpp/h should be renamed to something more sensible already :)
+
+#pragma once
+
+class CCpuID {public: CCpuID(); enum flag_t {mmx=1, ssemmx=2, ssefpu=4, sse2=8, _3dnow=16} m_flags;};
+extern CCpuID g_cpuid;
+
+extern bool BitBltFromI420ToI420(int w, int h, BYTE* dsty, BYTE* dstu, BYTE* dstv, int dstpitch, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch);
+extern bool BitBltFromI420ToYUY2(int w, int h, BYTE* dst, int dstpitch, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch, bool fInterlaced = false);
+extern bool BitBltFromI420ToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* srcy, BYTE* srcu, BYTE* srcv, int srcpitch /* TODO: , bool fInterlaced = false */);
+extern bool BitBltFromYUY2ToYUY2(int w, int h, BYTE* dst, int dstpitch, BYTE* src, int srcpitch);
+extern bool BitBltFromYUY2ToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* src, int srcpitch);
+extern bool BitBltFromRGBToRGB(int w, int h, BYTE* dst, int dstpitch, int dbpp, BYTE* src, int srcpitch, int sbpp);
+
+extern void DeinterlaceBlend(BYTE* dst, BYTE* src, DWORD rowbytes, DWORD h, DWORD dstpitch, DWORD srcpitch);
+extern void DeinterlaceBob(BYTE* dst, BYTE* src, DWORD rowbytes, DWORD h, DWORD dstpitch, DWORD srcpitch, bool topfield);
+
+extern void AvgLines8(BYTE* dst, DWORD h, DWORD pitch);
+extern void AvgLines555(BYTE* dst, DWORD h, DWORD pitch);
+extern void AvgLines565(BYTE* dst, DWORD h, DWORD pitch);
+
diff --git a/vsfilter/include/IFilterVersion.h b/vsfilter/include/IFilterVersion.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0e693058f7855c4213f1fdb87b94fe2a6323888
--- /dev/null
+++ b/vsfilter/include/IFilterVersion.h
@@ -0,0 +1,28 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+[uuid("A0DD28E1-61C0-4539-A7E6-14544AFF092E")]
+interface IFilterVersion : public IUnknown
+{
+	STDMETHOD_(DWORD, GetFilterVersion) () = 0;
+};
\ No newline at end of file
diff --git a/vsfilter/include/avisynth/avisynth1.h b/vsfilter/include/avisynth/avisynth1.h
new file mode 100644
index 0000000000000000000000000000000000000000..380f0584d720b0b6d25560abd6de69b576e3c6e0
--- /dev/null
+++ b/vsfilter/include/avisynth/avisynth1.h
@@ -0,0 +1,406 @@
+// Avisynth v1.0 beta.  Copyright 2000 Ben Rudiak-Gould.
+// http://www.math.berkeley.edu/~benrg/avisynth.html
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
+// http://www.gnu.org/copyleft/gpl.html .
+
+
+#pragma once
+
+#ifdef _MSC_VER
+  #include <crtdbg.h>
+#else
+  #define _ASSERTE(x) assert(x)
+  #include <assert.h>
+#endif
+
+
+enum { AVISYNTH_INTERFACE_VERSION = 1 };
+
+
+// I had problems with Premiere wanting 1-byte alignment for its structures,
+// so I now set the Avisynth struct alignment explicitly here.
+#pragma pack(push,8)
+
+
+// The VideoInfo struct holds global information about a clip (i.e.
+// information that does not depend on the frame number).  The GetVideoInfo
+// method in IClip returns this struct.
+
+struct VideoInfo {
+  int width, height;    // width=0 means no video
+  unsigned fps_numerator, fps_denominator;
+  int num_frames;
+  enum { UNKNOWN=0, BGR24=0x13, BGR32=0x14, YUY2=0x22 };
+  unsigned char pixel_type;
+  bool field_based;
+
+  int audio_samples_per_second;   // 0 means no audio
+  int num_audio_samples;
+  bool stereo, sixteen_bit;
+
+  // useful functions of the above
+  bool HasVideo() const { return !!width; }
+  bool HasAudio() const { return !!audio_samples_per_second; }
+  bool IsRGB() const { return !!(pixel_type&0x10); }
+  bool IsRGB24() const { return pixel_type == BGR24; }
+  bool IsRGB32() const { return pixel_type == BGR32; }
+  bool IsYUV() const { return !!(pixel_type&0x20); }
+  bool IsYUY2() const { return pixel_type == YUY2; }
+  int BytesFromPixels(int pixels) const { return pixels * (pixel_type&7); }
+  int RowSize() const { return BytesFromPixels(width); }
+  int BitsPerPixel() const { return (pixel_type&7) * 8; }
+  int BMPSize() const { return height * ((RowSize()+3) & -4); }
+  int AudioSamplesFromFrames(int frames) const { return int(__int64(frames) * audio_samples_per_second * fps_denominator / fps_numerator); }
+  int FramesFromAudioSamples(int samples) const { return int(__int64(samples) * fps_numerator / fps_denominator / audio_samples_per_second); }
+  int AudioSamplesFromBytes(int bytes) const { return bytes >> (stereo + sixteen_bit); }
+  int BytesFromAudioSamples(int samples) const { return samples << (stereo + sixteen_bit); }
+  int BytesPerAudioSample() const { return BytesFromAudioSamples(1); }
+
+  // useful mutator
+  void SetFPS(unsigned numerator, unsigned denominator) {
+    unsigned x=numerator, y=denominator;
+    while (y) {   // find gcd
+      unsigned t = x%y; x = y; y = t;
+    }
+    fps_numerator = numerator/x;
+    fps_denominator = denominator/x;
+  }
+};
+
+
+// VideoFrameBuffer holds information about a memory block which is used
+// for video data.  For efficiency, instances of this class are not deleted
+// when the refcount reaches zero; instead they're stored in a linked list
+// to be reused.  The instances are deleted when the corresponding AVS
+// file is closed.
+
+class VideoFrameBuffer {
+  unsigned char* const data;
+  const int data_size;
+  // sequence_number is incremented every time the buffer is changed, so
+  // that stale views can tell they're no longer valid.
+  long sequence_number;
+
+  friend class VideoFrame;
+  friend class Cache;
+  long refcount;
+
+public:
+  VideoFrameBuffer(int size);
+  VideoFrameBuffer();
+  ~VideoFrameBuffer();
+
+  const unsigned char* GetReadPtr() const { return data; }
+  unsigned char* GetWritePtr() { ++sequence_number; return data; }
+  int GetDataSize() { return data_size; }
+  int GetSequenceNumber() { return sequence_number; }
+  int GetRefcount() { return refcount; }
+};
+
+
+class IClip;
+class PClip;
+class PVideoFrame;
+class IScriptEnvironment;
+class AVSValue;
+
+
+// VideoFrame holds a "window" into a VideoFrameBuffer.  Operator new
+// is overloaded to recycle class instances.
+
+class VideoFrame {
+  int refcount;
+  VideoFrameBuffer* const vfb;
+  const int offset, pitch, row_size, height;
+
+  friend class PVideoFrame;
+  void AddRef() { ++refcount; }
+  void Release() { if (refcount==1) --vfb->refcount; --refcount; }
+
+  friend class ScriptEnvironment;
+  friend class Cache;
+
+  VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height);
+
+  void* operator new(unsigned size);
+
+public:
+  int GetPitch() const { return pitch; }
+  int GetRowSize() const { return row_size; }
+  int GetHeight() const { return height; }
+
+  // generally you shouldn't use these two 
+  VideoFrameBuffer* GetFrameBuffer() const { return vfb; }
+  int GetOffset() const { return offset; }
+
+  // in plugins use env->SubFrame()
+  VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
+
+  const unsigned char* GetReadPtr() const { return vfb->GetReadPtr() + offset; }
+
+  bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
+
+  unsigned char* GetWritePtr() const {
+    return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
+  }
+
+  ~VideoFrame() { --vfb->refcount; }
+};
+
+
+// Base class for all filters.
+class IClip {
+  friend class PClip;
+  friend class AVSValue;
+  int refcnt;
+  void AddRef() { ++refcnt; }
+  void Release() { if (!--refcnt) delete this; }
+public:
+  IClip() : refcnt(0) {}
+
+  virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
+
+  virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
+  virtual bool __stdcall GetParity(int n) = 0;  // return field parity if field_based, else parity of first field in frame
+  virtual void __stdcall GetAudio(void* buf, int start, int count, IScriptEnvironment* env) = 0;  // start and count are in samples
+  virtual const VideoInfo& __stdcall GetVideoInfo() = 0;
+  virtual __stdcall ~IClip() {}
+};
+
+
+// smart pointer to IClip
+class PClip {
+
+  IClip* p;
+
+  IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
+  friend class AVSValue;
+  friend class VideoFrame;
+
+  void Init(IClip* x) {
+    if (x) x->AddRef();
+    p=x;
+  }
+  void Set(IClip* x) {
+    if (x) x->AddRef();
+    if (p) p->Release();
+    p=x;
+  }
+
+public:
+  PClip() { p = 0; }
+  PClip(const PClip& x) { Init(x.p); }
+  PClip(IClip* x) { Init(x); }
+  void operator=(IClip* x) { Set(x); }
+  void operator=(const PClip& x) { Set(x.p); }
+
+  IClip* operator->() const { return p; }
+
+  // useful in conditional expressions
+  operator void*() const { return p; }
+  bool operator!() const { return !p; }
+
+  ~PClip() { if (p) p->Release(); }
+};
+
+
+// smart pointer to VideoFrame
+class PVideoFrame {
+
+  VideoFrame* p;
+
+  void Init(VideoFrame* x) {
+    if (x) x->AddRef();
+    p=x;
+  }
+  void Set(VideoFrame* x) {
+    if (x) x->AddRef();
+    if (p) p->Release();
+    p=x;
+  }
+
+public:
+  PVideoFrame() { p = 0; }
+  PVideoFrame(const PVideoFrame& x) { Init(x.p); }
+  PVideoFrame(VideoFrame* x) { Init(x); }
+  void operator=(VideoFrame* x) { Set(x); }
+  void operator=(const PVideoFrame& x) { Set(x.p); }
+
+  VideoFrame* operator->() const { return p; }
+
+  // for conditional expressions
+  operator void*() const { return p; }
+  bool operator!() const { return !p; }
+
+  ~PVideoFrame() { if (p) p->Release(); }
+};
+
+
+class AVSValue {
+public:
+
+  AVSValue() { type = 'v'; }
+  AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); }
+  AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
+  AVSValue(bool b) { type = 'b'; boolean = b; }
+  AVSValue(int i) { type = 'i'; integer = i; }
+  AVSValue(float f) { type = 'f'; floating_pt = f; }
+  AVSValue(double f) { type = 'f'; floating_pt = float(f); }
+  AVSValue(const char* s) { type = 's'; string = s; }
+  AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; }
+  AVSValue(const AVSValue& v) { Assign(&v, true); }
+
+  ~AVSValue() { if (IsClip() && clip) clip->Release(); }
+  AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; }
+
+  // Note that we transparently allow 'int' to be treated as 'float'.
+  // There are no int<->bool conversions, though.
+
+  bool Defined() const { return type != 'v'; }
+  bool IsClip() const { return type == 'c'; }
+  bool IsBool() const { return type == 'b'; }
+  bool IsInt() const { return type == 'i'; }
+  bool IsFloat() const { return type == 'f' || type == 'i'; }
+  bool IsString() const { return type == 's'; }
+  bool IsArray() const { return type == 'a'; }
+
+  PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; }
+  bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
+  int AsInt() const { _ASSERTE(IsInt()); return integer; }
+  const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; }
+  double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; }
+
+  bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; }
+  int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; }
+  double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; }
+  const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; }
+
+  int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; }
+  const AVSValue& operator[](int index) const {
+    _ASSERTE(IsArray() && index>=0 && index<array_size);
+    return (IsArray() && index>=0 && index<array_size) ? array[index] : *this;
+  }
+
+private:
+
+  short type;  // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, or 'v'oid
+  short array_size;
+  union {
+    IClip* clip;
+    bool boolean;
+    int integer;
+    float floating_pt;
+    const char* string;
+    const AVSValue* array;
+  };
+
+  void Assign(const AVSValue* src, bool init) {
+    if (src->IsClip() && src->clip)
+      src->clip->AddRef();
+    if (!init && IsClip() && clip)
+      clip->Release();
+    // make sure this copies the whole struct!
+    ((__int32*)this)[0] = ((__int32*)src)[0];
+    ((__int32*)this)[1] = ((__int32*)src)[1];
+  }
+};
+
+
+// instantiable null filter
+class GenericVideoFilter : public IClip {
+protected:
+  PClip child;
+  VideoInfo vi;
+public:
+  GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
+  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
+  void __stdcall GetAudio(void* buf, int start, int count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); }
+  const VideoInfo& __stdcall GetVideoInfo() { return vi; }
+  bool __stdcall GetParity(int n) { return child->GetParity(n); }
+};
+
+
+class AvisynthError /* exception */ {
+public:
+  const char* const msg;
+  AvisynthError(const char* _msg) : msg(_msg) {}
+};
+
+
+// For GetCPUFlags.  These are the same as in VirtualDub.
+enum {
+  CPUF_FORCE			= 0x01,
+  CPUF_FPU			= 0x02,
+  CPUF_MMX			= 0x04,
+  CPUF_INTEGER_SSE	= 0x08,		// Athlon MMX extensions or Intel SSE
+  CPUF_SSE			= 0x10,		// Full SSE (PIII)
+  CPUF_SSE2			= 0x20,		// (PIV)
+  CPUF_3DNOW			= 0x40,
+  CPUF_3DNOW_EXT		= 0x80,		// Athlon 3DNow! extensions
+};
+
+
+class IScriptEnvironment {
+public:
+  virtual __stdcall ~IScriptEnvironment() {}
+
+  virtual /*static*/ long __stdcall GetCPUFlags() = 0;
+
+  virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
+  virtual char* __stdcall Sprintf(const char* fmt, ...) = 0;
+  // note: val is really a va_list; I hope everyone typedefs va_list to a pointer
+  virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0;
+
+  __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0;
+
+  class NotFound /*exception*/ {};  // thrown by Invoke and GetVar
+
+  typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env);
+
+  virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0;
+  virtual bool __stdcall FunctionExists(const char* name) = 0;
+  virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0;
+
+  virtual AVSValue __stdcall GetVar(const char* name) = 0;
+  virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0;
+  virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0;
+
+  virtual void __stdcall PushContext(int level=0) = 0;
+  virtual void __stdcall PopContext() = 0;
+
+  // align should be 4 or 8
+  virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=8) = 0;
+
+  virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0;
+
+  virtual /*static*/ void __stdcall BitBlt(unsigned char* dstp, int dst_pitch, const unsigned char* srcp, int src_pitch, int row_size, int height) = 0;
+
+  typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env);
+  virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0;
+
+  virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
+
+  virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
+};
+
+
+// avisynth.dll exports this; it's a way to use it as a library, without
+// writing an AVS script or without going through AVIFile.
+IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
+
+
+#pragma pack(pop)
+
diff --git a/vsfilter/include/avisynth/avisynth25.h b/vsfilter/include/avisynth/avisynth25.h
new file mode 100644
index 0000000000000000000000000000000000000000..08ef55a6d948dae14408c6a66fda9c47c449de48
--- /dev/null
+++ b/vsfilter/include/avisynth/avisynth25.h
@@ -0,0 +1,670 @@
+// Avisynth v2.5 alpha.  Copyright 2002 Ben Rudiak-Gould et al.
+// http://www.avisynth.org
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
+// http://www.gnu.org/copyleft/gpl.html .
+//
+// Linking Avisynth statically or dynamically with other modules is making a
+// combined work based on Avisynth.  Thus, the terms and conditions of the GNU
+// General Public License cover the whole combination.
+//
+// As a special exception, the copyright holders of Avisynth give you
+// permission to link Avisynth with independent modules that communicate with
+// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
+// terms of these independent modules, and to copy and distribute the
+// resulting combined work under terms of your choice, provided that
+// every copy of the combined work is accompanied by a complete copy of
+// the source code of Avisynth (the version of Avisynth used to produce the
+// combined work), being distributed under the terms of the GNU General
+// Public License plus this exception.  An independent module is a module
+// which is not derived from or based on Avisynth, such as 3rd-party filters,
+// import and export plugins, or graphical user interfaces.
+
+
+#pragma once
+
+enum { AVISYNTH_INTERFACE_VERSION = 2 };
+
+
+/* Define all types necessary for interfacing with avisynth.dll
+   Moved from internal.h */
+
+// Win32 API macros, notably the types BYTE, DWORD, ULONG, etc. 
+#include <windef.h>  
+
+// COM interface macros
+#include <objbase.h>
+
+// Raster types used by VirtualDub & Avisynth
+#define in64 (__int64)(unsigned short)
+typedef unsigned long	Pixel;    // this will break on 64-bit machines!
+typedef unsigned long	Pixel32;
+typedef unsigned char Pixel8;
+typedef long			PixCoord;
+typedef	long			PixDim;
+typedef	long			PixOffset;
+
+
+/* Compiler-specific crap */
+
+// Tell MSVC to stop precompiling here
+#ifdef _MSC_VER
+//  #pragma hdrstop
+#endif
+
+// Set up debugging macros for MS compilers; for others, step down to the
+// standard <assert.h> interface
+#ifdef _MSC_VER
+  #include <crtdbg.h>
+#else
+  #define _RPT0(a,b) ((void)0)
+  #define _RPT1(a,b,c) ((void)0)
+  #define _RPT2(a,b,c,d) ((void)0)
+  #define _RPT3(a,b,c,d,e) ((void)0)
+  #define _RPT4(a,b,c,d,e,f) ((void)0)
+  
+  #define _ASSERTE(x) assert(x)
+  #include <assert.h>
+#endif
+
+
+
+// I had problems with Premiere wanting 1-byte alignment for its structures,
+// so I now set the Avisynth struct alignment explicitly here.
+#pragma pack(push,8)
+
+#define FRAME_ALIGN 16 
+// Default frame alignment is 16 bytes, to help P4, when using SSE2
+
+// The VideoInfo struct holds global information about a clip (i.e.
+// information that does not depend on the frame number).  The GetVideoInfo
+// method in IClip returns this struct.
+
+// Audio Sample information
+typedef float SFLOAT;
+
+enum {SAMPLE_INT8  = 1<<0,
+        SAMPLE_INT16 = 1<<1, 
+        SAMPLE_INT24 = 1<<2,    // Int24 is a very stupid thing to code, but it's supported by some hardware.
+        SAMPLE_INT32 = 1<<3,
+        SAMPLE_FLOAT = 1<<4};
+
+enum {
+   PLANAR_Y=1<<0,
+   PLANAR_U=1<<1,
+   PLANAR_V=1<<2,
+   PLANAR_ALIGNED=1<<3,
+   PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED,
+   PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED,
+   PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED,
+  };
+
+struct VideoInfo {
+  int width, height;    // width=0 means no video
+  unsigned fps_numerator, fps_denominator;
+  int num_frames;
+  // This is more extensible than previous versions. More properties can be added seeminglesly.
+
+  // Colorspace properties.
+  enum {
+    CS_BGR = 1<<28,  
+    CS_YUV = 1<<29,
+    CS_INTERLEAVED = 1<<30,
+    CS_PLANAR = 1<<31
+  };
+
+  // Specific colorformats
+  enum { CS_UNKNOWN = 0,
+         CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED,
+         CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED,
+         CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED,
+         CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR,  // y-v-u, planar
+         CS_I420 = 1<<4 | CS_YUV | CS_PLANAR,  // y-u-v, planar
+         CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR  // same as above
+         };
+  int pixel_type;                // changed to int as of 2.5
+  
+
+  int audio_samples_per_second;   // 0 means no audio
+  int sample_type;                // as of 2.5
+  __int64 num_audio_samples;      // changed as of 2.5
+  int nchannels;                  // as of 2.5
+
+  // Imagetype properties
+
+  int image_type;
+
+  enum {
+    IT_BFF = 1<<0,
+    IT_TFF = 1<<1,
+    IT_FIELDBASED = 1<<2
+  };
+
+  // useful functions of the above
+  bool HasVideo() const { return (width!=0); }
+  bool HasAudio() const { return (audio_samples_per_second!=0); }
+  bool IsRGB() const { return !!(pixel_type&CS_BGR); }
+  bool IsRGB24() const { return (pixel_type&CS_BGR24)==CS_BGR24; } // Clear out additional properties
+  bool IsRGB32() const { return (pixel_type & CS_BGR32) == CS_BGR32 ; }
+  bool IsYUV() const { return !!(pixel_type&CS_YUV ); }
+  bool IsYUY2() const { return (pixel_type & CS_YUY2) == CS_YUY2; }  
+  bool IsYV12() const { return ((pixel_type & CS_YV12) == CS_YV12)||((pixel_type & CS_I420) == CS_I420); }
+  bool IsColorSpace(int c_space) const { return ((pixel_type & c_space) == c_space); }
+  bool Is(int property) const { return ((pixel_type & property)==property ); }
+  bool IsPlanar() const { return !!(pixel_type & CS_PLANAR); }
+  bool IsFieldBased() const { return !!(image_type & IT_FIELDBASED); }
+  bool IsParityKnown() const { return ((image_type & IT_FIELDBASED)&&(image_type & (IT_BFF||IT_TFF))); }
+  bool IsBFF() const { return !!(pixel_type & IT_BFF); }
+  bool IsTFF() const { return !!(pixel_type & IT_TFF); }
+  
+  bool IsVPlaneFirst() const {return ((pixel_type & CS_YV12) == CS_YV12); }  // Don't use this 
+  int BytesFromPixels(int pixels) const { return pixels * (BitsPerPixel()>>3); }   // Will not work on planar images, but will return only luma planes
+  int RowSize() const { return BytesFromPixels(width); }  // Also only returns first plane on planar images
+  int BMPSize() const { if (IsPlanar()) {int p = height * ((RowSize()+3) & ~3); p+=p>>1; return p;  } return height * ((RowSize()+3) & ~3); }
+  __int64 AudioSamplesFromFrames(__int64 frames) const { return (__int64(frames) * audio_samples_per_second * fps_denominator / fps_numerator); }
+  int FramesFromAudioSamples(__int64 samples) const { return (int)(samples * (__int64)fps_numerator / (__int64)fps_denominator / (__int64)audio_samples_per_second); }
+  __int64 AudioSamplesFromBytes(__int64 bytes) const { return bytes / BytesPerAudioSample(); }
+  __int64 BytesFromAudioSamples(__int64 samples) const { return samples * BytesPerAudioSample(); }
+  int AudioChannels() const { return nchannels; }
+  int SampleType() const{ return sample_type;}
+  int SamplesPerSecond() const { return audio_samples_per_second; }
+  int BytesPerAudioSample() const { return nchannels*BytesPerChannelSample();}
+  void SetFieldBased(bool isfieldbased)  { if (isfieldbased) image_type|=IT_FIELDBASED; else  image_type&=~IT_FIELDBASED; }
+  void Set(int property)  { image_type|=property; }
+  void Clear(int property)  { image_type&=~property; }
+
+  int BitsPerPixel() const { 
+    switch (pixel_type) {
+      case CS_BGR24:
+        return 24;
+      case CS_BGR32:
+        return 32;
+      case CS_YUY2:
+        return 16;
+      case CS_YV12:
+      case CS_I420:
+        return 12;
+      default:
+        return 0;
+    }
+  }
+  int BytesPerChannelSample() const { 
+    switch (sample_type) {
+    case SAMPLE_INT8:
+      return sizeof(signed char);
+    case SAMPLE_INT16:
+      return sizeof(signed short);
+    case SAMPLE_INT24:
+      return 3;
+    case SAMPLE_INT32:
+      return sizeof(signed int);
+    case SAMPLE_FLOAT:
+      return sizeof(SFLOAT);
+    default:
+      _ASSERTE("Sample type not recognized!");
+      return 0;
+    }
+  }
+
+  // useful mutator
+  void SetFPS(unsigned numerator, unsigned denominator) {
+    unsigned x=numerator, y=denominator;
+    while (y) {   // find gcd
+      unsigned t = x%y; x = y; y = t;
+    }
+    fps_numerator = numerator/x;
+    fps_denominator = denominator/x;
+  }
+};
+
+enum {
+  FILTER_TYPE=1,
+  FILTER_INPUT_COLORSPACE=2,
+  FILTER_OUTPUT_TYPE=9,
+  FILTER_NAME=4,
+  FILTER_AUTHOR=5,
+  FILTER_VERSION=6,
+  FILTER_ARGS=7,
+  FILTER_ARGS_INFO=8,
+  FILTER_ARGS_DESCRIPTION=10,
+  FILTER_DESCRIPTION=11,
+};
+enum {  //SUBTYPES
+  FILTER_TYPE_AUDIO=1,
+  FILTER_TYPE_VIDEO=2,
+  FILTER_OUTPUT_TYPE_SAME=3,
+  FILTER_OUTPUT_TYPE_DIFFERENT=4,
+};
+
+
+
+// VideoFrameBuffer holds information about a memory block which is used
+// for video data.  For efficiency, instances of this class are not deleted
+// when the refcount reaches zero; instead they're stored in a linked list
+// to be reused.  The instances are deleted when the corresponding AVS
+// file is closed.
+
+class VideoFrameBuffer {
+  BYTE* const data;
+  const int data_size;
+  // sequence_number is incremented every time the buffer is changed, so
+  // that stale views can tell they're no longer valid.
+  long sequence_number;
+
+  friend class VideoFrame;
+  friend class Cache;
+  long refcount;
+
+public:
+  VideoFrameBuffer(int size);
+  VideoFrameBuffer();
+  ~VideoFrameBuffer();
+
+  const BYTE* GetReadPtr() const { return data; }
+  BYTE* GetWritePtr() { ++sequence_number; return data; }
+  int GetDataSize() { return data_size; }
+  int GetSequenceNumber() { return sequence_number; }
+  int GetRefcount() { return refcount; }
+};
+
+
+class IClip;
+class PClip;
+class PVideoFrame;
+class IScriptEnvironment;
+class AVSValue;
+
+
+// VideoFrame holds a "window" into a VideoFrameBuffer.  Operator new
+// is overloaded to recycle class instances.
+
+class VideoFrame {
+  int refcount;
+  VideoFrameBuffer* const vfb;
+  const int offset, pitch, row_size, height, offsetU, offsetV, pitchUV;  // U&V offsets are from top of picture.
+
+  friend class PVideoFrame;
+  void AddRef() { ++refcount; }
+  void Release() { if (refcount==1) --vfb->refcount; --refcount; }
+
+  friend class ScriptEnvironment;
+  friend class Cache;
+
+  VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height);
+  VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV);
+
+  void* operator new(unsigned size);
+// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard!
+public:
+  int GetPitch() const { return pitch; }
+  int GetPitch(int plane) const { switch (plane) {case PLANAR_U: case PLANAR_V: return pitchUV;} return pitch; }
+  int GetRowSize() const { return row_size; }
+  int GetRowSize(int plane) const { 
+    switch (plane) {
+    case PLANAR_U: case PLANAR_V: if (pitchUV) return row_size>>1; else return 0;
+    case PLANAR_U_ALIGNED: case PLANAR_V_ALIGNED: 
+      if (pitchUV) { 
+        int r = ((row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)) )>>1; // Aligned rowsize
+        if (r<=pitchUV) 
+          return r; 
+        return row_size>>1; 
+      } else return 0;
+    case PLANAR_Y_ALIGNED:
+      int r = (row_size+FRAME_ALIGN-1)&(~(FRAME_ALIGN-1)); // Aligned rowsize
+      if (r<=pitch) 
+        return r; 
+      return row_size;
+    }
+    return row_size; }
+  int GetHeight() const { return height; }
+  int GetHeight(int plane) const {  switch (plane) {case PLANAR_U: case PLANAR_V: if (pitchUV) return height>>1; return 0;} return height; }
+
+  // generally you shouldn't use these three
+  VideoFrameBuffer* GetFrameBuffer() const { return vfb; }
+  int GetOffset() const { return offset; }
+  int GetOffset(int plane) const { switch (plane) {case PLANAR_U: return offsetU;case PLANAR_V: return offsetV;default: return offset;}; }
+
+  // in plugins use env->SubFrame()
+  VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const;
+  VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const;
+
+
+  const BYTE* GetReadPtr() const { return vfb->GetReadPtr() + offset; }
+  const BYTE* GetReadPtr(int plane) const { return vfb->GetReadPtr() + GetOffset(plane); }
+
+  bool IsWritable() const { return (refcount == 1 && vfb->refcount == 1); }
+
+  BYTE* GetWritePtr() const {
+    return IsWritable() ? (vfb->GetWritePtr() + offset) : 0;
+  }
+
+  BYTE* GetWritePtr(int plane) const {
+    if (plane==PLANAR_Y)
+      return IsWritable() ? vfb->GetWritePtr() + GetOffset(plane) : 0;
+    return vfb->data + GetOffset(plane);
+  }
+
+  ~VideoFrame() { --vfb->refcount; }
+};
+
+enum {
+  CACHE_NOTHING=0,
+  CACHE_RANGE=1 };
+
+// Base class for all filters.
+class IClip {
+  friend class PClip;
+  friend class AVSValue;
+  int refcnt;
+  void AddRef() { ++refcnt; }
+  void Release() { if (!--refcnt) delete this; }
+public:
+  IClip() : refcnt(0) {}
+
+  virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; }
+  
+  virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0;
+  virtual bool __stdcall GetParity(int n) = 0;  // return field parity if field_based, else parity of first field in frame
+  virtual void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) = 0;  // start and count are in samples
+  virtual void __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ;  // We do not pass cache requests upwards, only to the next filter.
+  virtual const VideoInfo& __stdcall GetVideoInfo() = 0;
+  virtual __stdcall ~IClip() {}
+};
+
+
+// smart pointer to IClip
+class PClip {
+
+  IClip* p;
+
+  IClip* GetPointerWithAddRef() const { if (p) p->AddRef(); return p; }
+  friend class AVSValue;
+  friend class VideoFrame;
+
+  void Init(IClip* x) {
+    if (x) x->AddRef();
+    p=x;
+  }
+  void Set(IClip* x) {
+    if (x) x->AddRef();
+    if (p) p->Release();
+    p=x;
+  }
+
+public:
+  PClip() { p = 0; }
+  PClip(const PClip& x) { Init(x.p); }
+  PClip(IClip* x) { Init(x); }
+  void operator=(IClip* x) { Set(x); }
+  void operator=(const PClip& x) { Set(x.p); }
+
+  IClip* operator->() const { return p; }
+
+  // useful in conditional expressions
+  operator void*() const { return p; }
+  bool operator!() const { return !p; }
+
+  ~PClip() { if (p) p->Release(); }
+};
+
+
+// smart pointer to VideoFrame
+class PVideoFrame {
+
+  VideoFrame* p;
+
+  void Init(VideoFrame* x) {
+    if (x) x->AddRef();
+    p=x;
+  }
+  void Set(VideoFrame* x) {
+    if (x) x->AddRef();
+    if (p) p->Release();
+    p=x;
+  }
+
+public:
+  PVideoFrame() { p = 0; }
+  PVideoFrame(const PVideoFrame& x) { Init(x.p); }
+  PVideoFrame(VideoFrame* x) { Init(x); }
+  void operator=(VideoFrame* x) { Set(x); }
+  void operator=(const PVideoFrame& x) { Set(x.p); }
+
+  VideoFrame* operator->() const { return p; }
+
+  // for conditional expressions
+  operator void*() const { return p; }
+  bool operator!() const { return !p; }
+
+  ~PVideoFrame() { if (p) p->Release(); }
+};
+
+
+class AVSValue {
+public:
+
+  AVSValue() { type = 'v'; }
+  AVSValue(IClip* c) { type = 'c'; clip = c; if (c) c->AddRef(); }
+  AVSValue(const PClip& c) { type = 'c'; clip = c.GetPointerWithAddRef(); }
+  AVSValue(bool b) { type = 'b'; boolean = b; }
+  AVSValue(int i) { type = 'i'; integer = i; }
+//  AVSValue(__int64 l) { type = 'l'; longlong = l; }
+  AVSValue(float f) { type = 'f'; floating_pt = f; }
+  AVSValue(double f) { type = 'f'; floating_pt = float(f); }
+  AVSValue(const char* s) { type = 's'; string = s; }
+  AVSValue(const AVSValue* a, int size) { type = 'a'; array = a; array_size = size; }
+  AVSValue(const AVSValue& v) { Assign(&v, true); }
+
+  ~AVSValue() { if (IsClip() && clip) clip->Release(); }
+  AVSValue& operator=(const AVSValue& v) { Assign(&v, false); return *this; }
+
+  // Note that we transparently allow 'int' to be treated as 'float'.
+  // There are no int<->bool conversions, though.
+
+  bool Defined() const { return type != 'v'; }
+  bool IsClip() const { return type == 'c'; }
+  bool IsBool() const { return type == 'b'; }
+  bool IsInt() const { return type == 'i'; }
+//  bool IsLong() const { return (type == 'l'|| type == 'i'); }
+  bool IsFloat() const { return type == 'f' || type == 'i'; }
+  bool IsString() const { return type == 's'; }
+  bool IsArray() const { return type == 'a'; }
+
+  PClip AsClip() const { _ASSERTE(IsClip()); return IsClip()?clip:0; }
+  bool AsBool() const { _ASSERTE(IsBool()); return boolean; }
+  int AsInt() const { _ASSERTE(IsInt()); return integer; }   
+//  int AsLong() const { _ASSERTE(IsLong()); return longlong; } 
+  const char* AsString() const { _ASSERTE(IsString()); return IsString()?string:0; }
+  double AsFloat() const { _ASSERTE(IsFloat()); return IsInt()?integer:floating_pt; }
+
+  bool AsBool(bool def) const { _ASSERTE(IsBool()||!Defined()); return IsBool() ? boolean : def; }
+  int AsInt(int def) const { _ASSERTE(IsInt()||!Defined()); return IsInt() ? integer : def; }
+  double AsFloat(double def) const { _ASSERTE(IsFloat()||!Defined()); return IsInt() ? integer : type=='f' ? floating_pt : def; }
+  const char* AsString(const char* def) const { _ASSERTE(IsString()||!Defined()); return IsString() ? string : def; }
+
+  int ArraySize() const { _ASSERTE(IsArray()); return IsArray()?array_size:1; }
+  const AVSValue& operator[](int index) const {
+    _ASSERTE(IsArray() && index>=0 && index<array_size);
+    return (IsArray() && index>=0 && index<array_size) ? array[index] : *this;
+  }
+
+private:
+
+  short type;  // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or 'l'ong
+  short array_size;
+  union {
+    IClip* clip;
+    bool boolean;
+    int integer;
+    float floating_pt;
+    const char* string;
+    const AVSValue* array;
+//    __int64 longlong;
+  };
+
+  void Assign(const AVSValue* src, bool init) {
+    if (src->IsClip() && src->clip)
+      src->clip->AddRef();
+    if (!init && IsClip() && clip)
+      clip->Release();
+    // make sure this copies the whole struct!
+    ((__int32*)this)[0] = ((__int32*)src)[0];
+    ((__int32*)this)[1] = ((__int32*)src)[1];
+  }
+};
+
+
+// instantiable null filter
+class GenericVideoFilter : public IClip {
+protected:
+  PClip child;
+  VideoInfo vi;
+public:
+  GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); }
+  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); }
+  void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); }
+  const VideoInfo& __stdcall GetVideoInfo() { return vi; }
+  bool __stdcall GetParity(int n) { return child->GetParity(n); }
+  void __stdcall SetCacheHints(int cachehints,int frame_range) { } ;  // We do not pass cache requests upwards, only to the next filter.
+};
+
+
+class AvisynthError /* exception */ {
+public:
+  const char* const msg;
+  AvisynthError(const char* _msg) : msg(_msg) {}
+};
+
+
+// For GetCPUFlags.  These are backwards-compatible with those in VirtualDub.
+enum {                    
+                    /* slowest CPU to support extension */
+  CPUF_FORCE			  = 0x01,   // N/A
+  CPUF_FPU			    = 0x02,   // 386/486DX
+  CPUF_MMX			    = 0x04,   // P55C, K6, PII
+  CPUF_INTEGER_SSE	= 0x08,		// PIII, Athlon
+  CPUF_SSE			    = 0x10,		// PIII, Athlon XP/MP
+  CPUF_SSE2			    = 0x20,		// PIV, Hammer
+  CPUF_3DNOW			  = 0x40,   // K6-2
+  CPUF_3DNOW_EXT		= 0x80,		// Athlon
+  CPUF_X86_64       = 0xA0,   // Hammer (note: equiv. to 3DNow + SSE2, which only Hammer
+                              //         will have anyway)
+};
+#define MAX_INT 0x7fffffff
+#define MIN_INT 0x80000000
+
+
+class ConvertAudio : public GenericVideoFilter 
+/**
+  * Helper class to convert audio to any format
+ **/
+{
+public:
+  ConvertAudio(PClip _clip, int prefered_format);
+  void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env);
+
+  static PClip Create(PClip clip, int sample_type, int prefered_type);
+  static AVSValue __cdecl Create_float(AVSValue args, void*, IScriptEnvironment*);
+  static AVSValue __cdecl Create_32bit(AVSValue args, void*, IScriptEnvironment*);
+  static AVSValue __cdecl Create_16bit(AVSValue args, void*, IScriptEnvironment*);
+  static AVSValue __cdecl Create_8bit(AVSValue args, void*, IScriptEnvironment*);
+  virtual ~ConvertAudio()
+  {if (tempbuffer_size) {delete[] tempbuffer;tempbuffer_size=0;}}
+private:
+void ConvertAudio::convertToFloat(char* inbuf, float* outbuf, char sample_type, int count);
+void ConvertAudio::convertFromFloat(float* inbuf, void* outbuf, char sample_type, int count);
+
+  __inline int Saturate_int8(float n);
+  __inline short Saturate_int16(float n);
+  __inline int Saturate_int24(float n);
+  __inline int Saturate_int32(float n);
+
+  char src_format;
+  char dst_format;
+  int src_bps;
+  char *tempbuffer;
+  SFLOAT *floatbuffer;
+  int tempbuffer_size;
+};
+
+class AlignPlanar : public GenericVideoFilter {
+public:
+  AlignPlanar(PClip _clip);
+  static PClip Create(PClip clip);
+  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
+};
+
+class FillBorder : public GenericVideoFilter {
+public:
+  FillBorder(PClip _clip);
+  static PClip Create(PClip clip);
+  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
+};
+
+class IScriptEnvironment {
+public:
+  virtual __stdcall ~IScriptEnvironment() {}
+
+  virtual /*static*/ long __stdcall GetCPUFlags() = 0;
+
+  virtual char* __stdcall SaveString(const char* s, int length = -1) = 0;
+  virtual char* __stdcall Sprintf(const char* fmt, ...) = 0;
+  // note: val is really a va_list; I hope everyone typedefs va_list to a pointer
+  virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0;
+
+  __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0;
+
+  class NotFound /*exception*/ {};  // thrown by Invoke and GetVar
+
+  typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env);
+
+  virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0;
+  virtual bool __stdcall FunctionExists(const char* name) = 0;
+  virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char** arg_names=0) = 0;
+
+  virtual AVSValue __stdcall GetVar(const char* name) = 0;
+  virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0;
+  virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0;
+
+  virtual void __stdcall PushContext(int level=0) = 0;
+  virtual void __stdcall PopContext() = 0;
+
+  // align should be 4 or 8
+  virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0;
+
+  virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0;
+
+  virtual /*static*/ void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0;
+
+  typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env);
+  virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0;
+
+  virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0;
+
+  virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0;
+
+	virtual int __stdcall SetMemoryMax(int mem) = 0;
+
+  virtual int __stdcall SetWorkingDir(const char * newdir) = 0;
+
+};
+
+
+// avisynth.dll exports this; it's a way to use it as a library, without
+// writing an AVS script or without going through AVIFile.
+IScriptEnvironment* __stdcall CreateScriptEnvironment(int version = AVISYNTH_INTERFACE_VERSION);
+
+
+#pragma pack(pop)
diff --git a/vsfilter/include/decss/VobDec.h b/vsfilter/include/decss/VobDec.h
new file mode 100644
index 0000000000000000000000000000000000000000..689b4c4f07d9b57b6050c24a5c8126719618ee8e
--- /dev/null
+++ b/vsfilter/include/decss/VobDec.h
@@ -0,0 +1,21 @@
+#pragma once
+
+class CVobDec
+{
+	int m_lfsr0, m_lfsr1;
+
+	void ClockLfsr0Forward(int& lfsr0);
+	void ClockLfsr1Forward(int& lfsr1);
+	void ClockBackward(int& lfsr0, int& lfsr1);
+	void Salt(const BYTE salt[5], int& lfsr0, int& lfsr1);
+	int FindLfsr(const BYTE* crypt, int offset, const BYTE* plain);
+
+public:
+	CVobDec();
+	virtual ~CVobDec();
+
+	bool m_fFoundKey;
+
+	bool FindKey(BYTE* buff);
+	void Decrypt(BYTE* buff);
+};
diff --git a/vsfilter/include/decss/vobfile.h b/vsfilter/include/decss/vobfile.h
new file mode 100644
index 0000000000000000000000000000000000000000..4a3f43177fb7ac485a34f0e3558a1b3a0cf398b6
--- /dev/null
+++ b/vsfilter/include/decss/vobfile.h
@@ -0,0 +1,87 @@
+#pragma once
+
+#pragma warning(disable : 4200)
+
+#include <atlbase.h>
+#include <atlcoll.h>
+//#include <winioctl.h> // platform sdk
+#include "../winddk/ntddcdvd.h"
+
+class CDVDSession
+{
+protected:
+	HANDLE m_hDrive;
+
+	DVD_SESSION_ID m_session;
+	bool BeginSession();
+	void EndSession();
+
+	BYTE m_SessionKey[5];
+	bool Authenticate();
+
+	BYTE m_DiscKey[6], m_TitleKey[6];
+	bool GetDiscKey();
+	bool GetTitleKey(int lba, BYTE* pKey);
+
+public:
+	CDVDSession();
+	virtual ~CDVDSession();
+
+	bool Open(LPCTSTR path);
+	void Close();
+
+	operator HANDLE() {return m_hDrive;}
+	operator DVD_SESSION_ID() {return m_session;}
+
+	bool SendKey(DVD_KEY_TYPE KeyType, BYTE* pKeyData);
+	bool ReadKey(DVD_KEY_TYPE KeyType, BYTE* pKeyData, int lba = 0);
+};
+
+class CLBAFile : private CFile
+{
+public:
+	CLBAFile();
+	virtual ~CLBAFile();
+
+	bool IsOpen();
+
+	bool Open(LPCTSTR path);
+	void Close();
+
+	int GetLength();
+	int GetPosition();
+	int Seek(int lba);
+	bool Read(BYTE* buff);
+};
+
+class CVobFile : public CDVDSession
+{
+	// all files
+	typedef struct {CString fn; int size;} file_t;
+	CAtlArray<file_t> m_files;
+	int m_iFile;
+	int m_pos, m_size, m_offset;
+
+	// currently opened file
+	CLBAFile m_file;
+
+	// attribs
+	bool m_fDVD, m_fHasDiscKey, m_fHasTitleKey;
+
+public:
+	CVobFile();
+	virtual ~CVobFile();
+
+	bool IsDVD();
+	bool HasDiscKey(BYTE* key);
+	bool HasTitleKey(BYTE* key);
+
+	bool Open(CString fn, CAtlList<CString>& files /* out */); // vts ifo
+	bool Open(CAtlList<CString>& files, int offset = -1); // vts vobs, video vob offset in lba
+	void Close();
+
+	int GetLength();
+	int GetPosition();
+	int Seek(int pos);
+	bool Read(BYTE* buff);
+};
diff --git a/vsfilter/include/moreuuids.h b/vsfilter/include/moreuuids.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e4d037fea827cc5b86b13c4ca9dc3b62bea2748
--- /dev/null
+++ b/vsfilter/include/moreuuids.h
@@ -0,0 +1,449 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ *  Note: This interface was defined for the matroska container format 
+ *  originally, but can be implemented for other formats as well.
+ *
+ */
+
+#pragma once
+
+#include <dvdmedia.h>
+
+// 30323449-0000-0010-8000-00AA00389B71  'I420' == MEDIASUBTYPE_I420
+DEFINE_GUID(MEDIASUBTYPE_I420,
+0x30323449, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_DOLBY_AC3 0x2000
+// {00002000-0000-0010-8000-00aa00389b71}
+DEFINE_GUID(MEDIASUBTYPE_WAVE_DOLBY_AC3, 
+WAVE_FORMAT_DOLBY_AC3, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_DVD_DTS 0x2001
+// {00002001-0000-0010-8000-00aa00389b71}
+DEFINE_GUID(MEDIASUBTYPE_WAVE_DTS, 
+WAVE_FORMAT_DVD_DTS, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// Be compatible with 3ivx
+#define WAVE_FORMAT_AAC 0x00FF
+// {000000FF-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_AAC,
+WAVE_FORMAT_AAC, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// ... and also compatible with nero
+// btw, older nero parsers use a lower-case fourcc, newer upper-case (why can't it just offer both?)
+// {4134504D-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_MP4A,
+0x4134504D, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// {6134706D-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_mp4a,
+0x6134706D, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_MP3 0x0055
+// 00000055-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_MP3,
+WAVE_FORMAT_MP3, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_FLAC 0xF1AC
+// 0000F1AC-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_FLAC,
+WAVE_FORMAT_FLAC, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// {1541C5C0-CDDF-477d-BC0A-86F8AE7F8354}
+DEFINE_GUID(MEDIASUBTYPE_FLAC_FRAMED,
+0x1541c5c0, 0xcddf, 0x477d, 0xbc, 0xa, 0x86, 0xf8, 0xae, 0x7f, 0x83, 0x54);
+
+#define WAVE_FORMAT_TTA1 0x77A1
+// {000077A1-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_TTA1,
+WAVE_FORMAT_TTA1, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_WAVPACK4 0x5756
+// {00005756-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_WAVPACK4,
+WAVE_FORMAT_WAVPACK4, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// {DA5B82EE-6BD2-426f-BF1E-30112DA78AE1}
+DEFINE_GUID(MEDIASUBTYPE_SVCD_SUBPICTURE, 
+0xda5b82ee, 0x6bd2, 0x426f, 0xbf, 0x1e, 0x30, 0x11, 0x2d, 0xa7, 0x8a, 0xe1);
+
+// {7B57308F-5154-4c36-B903-52FE76E184FC}
+DEFINE_GUID(MEDIASUBTYPE_CVD_SUBPICTURE, 
+0x7b57308f, 0x5154, 0x4c36, 0xb9, 0x3, 0x52, 0xfe, 0x76, 0xe1, 0x84, 0xfc);
+
+// {0E3A2342-F6E2-4c91-BDAE-87C71EAD0D63}
+DEFINE_GUID(MEDIASUBTYPE_MPEG2_PVA, 
+0xe3a2342, 0xf6e2, 0x4c91, 0xbd, 0xae, 0x87, 0xc7, 0x1e, 0xad, 0xd, 0x63);
+
+// {6B6D0800-9ADA-11d0-A520-00A0D10129C0}
+DEFINE_GUID(CLSID_NetShowSource, 
+0x6b6d0800, 0x9ada, 0x11d0, 0xa5, 0x20, 0x0, 0xa0, 0xd1, 0x1, 0x29, 0xc0);
+
+// DirectShowMedia
+
+// {5E9C9EE0-2E4A-4f22-9906-7BBBB75AA2B6}
+DEFINE_GUID(MEDIASUBTYPE_DirectShowMedia, 
+0x5e9c9ee0, 0x2e4a, 0x4f22, 0x99, 0x6, 0x7b, 0xbb, 0xb7, 0x5a, 0xa2, 0xb6);
+
+// Dirac
+
+// {A29DA00F-A22B-40ea-98DE-2F7FECADA5DE}
+DEFINE_GUID(MEDIASUBTYPE_Dirac, 
+0xa29da00f, 0xa22b, 0x40ea, 0x98, 0xde, 0x2f, 0x7f, 0xec, 0xad, 0xa5, 0xde);
+
+// {64726376-0000-0010-8000-00AA00389B71}
+DEFINE_GUID(MEDIASUBTYPE_DiracVideo,
+0x64726376, 0x000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+// {D2667A7E-4055-4244-A65F-DDDDF2B74BD7}
+DEFINE_GUID(FORMAT_DiracVideoInfo, 
+0xd2667a7e, 0x4055, 0x4244, 0xa6, 0x5f, 0xdd, 0xdd, 0xf2, 0xb7, 0x4b, 0xd7);
+
+struct DIRACINFOHEADER
+{
+    VIDEOINFOHEADER2 hdr;
+    DWORD cbSequenceHeader;
+    DWORD dwSequenceHeader[1];
+};
+
+// MP4
+
+// {08E22ADA-B715-45ed-9D20-7B87750301D4}
+DEFINE_GUID(MEDIASUBTYPE_MP4, 
+0x8e22ada, 0xb715, 0x45ed, 0x9d, 0x20, 0x7b, 0x87, 0x75, 0x3, 0x1, 0xd4);
+
+// FLV
+
+// {F2FAC0F1-3852-4670-AAC0-9051D400AC54}
+DEFINE_GUID(MEDIASUBTYPE_FLV, 
+0xf2fac0f1, 0x3852, 0x4670, 0xaa, 0xc0, 0x90, 0x51, 0xd4, 0x0, 0xac, 0x54);
+
+// 34564c46-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_FLV4,
+0x34564c46, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 30365056-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_VP60,
+0x30365056, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 31365056-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_VP61,
+0x31365056, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 32365056-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_VP62,
+0x32365056, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+//
+// RealMedia
+//
+
+// {57428EC6-C2B2-44a2-AA9C-28F0B6A5C48E}
+DEFINE_GUID(MEDIASUBTYPE_RealMedia, 
+0x57428ec6, 0xc2b2, 0x44a2, 0xaa, 0x9c, 0x28, 0xf0, 0xb6, 0xa5, 0xc4, 0x8e);
+
+// 30315652-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RV10,
+0x30315652, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 30325652-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RV20,
+0x30325652, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 30335652-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RV30,
+0x30335652, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 30345652-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RV40,
+0x30345652, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 31345652-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RV41,
+0x31345652, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 345f3431-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_14_4,
+0x345f3431, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 385f3832-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_28_8,
+0x385f3832, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 43525441-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_ATRC,
+0x43525441, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 4b4f4f43-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_COOK,
+0x4b4f4f43, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 54454e44-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_DNET,
+0x54454e44, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 52504953-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_SIPR,
+0x52504953, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 43414152-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RAAC,
+0x43414152, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+// 50434152-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_RACP,
+0x50434152, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+
+enum 
+{
+	WAVE_FORMAT_14_4 = 0x2002,
+	WAVE_FORMAT_28_8 = 0x2003,
+	WAVE_FORMAT_ATRC = 0x0270, //WAVE_FORMAT_SONY_SCX,
+	WAVE_FORMAT_COOK = 0x2004,
+	WAVE_FORMAT_DNET = 0x2005,
+	WAVE_FORMAT_RAAC = 0x2006,
+	WAVE_FORMAT_RACP = 0x2007,
+	WAVE_FORMAT_SIPR = 0x0130, //WAVE_FORMAT_SIPROLAB_ACEPLNET,
+};
+
+//
+// PS2
+//
+
+#define WAVE_FORMAT_PS2_PCM 0xF521
+// 0000F521-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_PS2_PCM,
+WAVE_FORMAT_PS2_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+#define WAVE_FORMAT_PS2_ADPCM 0xF522
+// 0000F522-0000-0010-8000-00AA00389B71
+DEFINE_GUID(MEDIASUBTYPE_PS2_ADPCM,
+WAVE_FORMAT_PS2_ADPCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+struct WAVEFORMATEXPS2 : public WAVEFORMATEX
+{
+    DWORD dwInterleave;
+
+	struct WAVEFORMATEXPS2()
+	{
+		memset(this, 0, sizeof(*this)); 
+		cbSize = sizeof(WAVEFORMATEXPS2) - sizeof(WAVEFORMATEX);
+	}
+};
+
+// {4F3D3D21-6D7C-4f73-AA05-E397B5EAE0AA}
+DEFINE_GUID(MEDIASUBTYPE_PS2_SUB, 
+0x4f3d3d21, 0x6d7c, 0x4f73, 0xaa, 0x5, 0xe3, 0x97, 0xb5, 0xea, 0xe0, 0xaa);
+
+// Haali's video renderer
+
+// {760A8F35-97E7-479d-AAF5-DA9EFF95D751}
+DEFINE_GUID(CLSID_DXR,
+0x760a8f35, 0x97e7, 0x479d, 0xaa, 0xf5, 0xda, 0x9e, 0xff, 0x95, 0xd7, 0x51);
+
+//
+// Ogg
+//
+
+// f07e245f-5a1f-4d1e-8bff-dc31d84a55ab
+DEFINE_GUID(CLSID_OggSplitter,
+0xf07e245f, 0x5a1f, 0x4d1e, 0x8b, 0xff, 0xdc, 0x31, 0xd8, 0x4a, 0x55, 0xab);
+
+// {078C3DAA-9E58-4d42-9E1C-7C8EE79539C5}
+DEFINE_GUID(CLSID_OggSplitPropPage,
+0x78c3daa, 0x9e58, 0x4d42, 0x9e, 0x1c, 0x7c, 0x8e, 0xe7, 0x95, 0x39, 0xc5);
+
+// 8cae96b7-85b1-4605-b23c-17ff5262b296 
+DEFINE_GUID(CLSID_OggMux,
+0x8cae96b7, 0x85b1, 0x4605, 0xb2, 0x3c, 0x17, 0xff, 0x52, 0x62, 0xb2, 0x96);
+
+// {AB97AFC3-D08E-4e2d-98E0-AEE6D4634BA4}
+DEFINE_GUID(CLSID_OggMuxPropPage,
+0xab97afc3, 0xd08e, 0x4e2d, 0x98, 0xe0, 0xae, 0xe6, 0xd4, 0x63, 0x4b, 0xa4);
+
+// {889EF574-0656-4B52-9091-072E52BB1B80}
+DEFINE_GUID(CLSID_VorbisEnc,
+0x889ef574, 0x0656, 0x4b52, 0x90, 0x91, 0x07, 0x2e, 0x52, 0xbb, 0x1b, 0x80);
+
+// {c5379125-fd36-4277-a7cd-fab469ef3a2f}
+DEFINE_GUID(CLSID_VorbisEncPropPage,
+0xc5379125, 0xfd36, 0x4277, 0xa7, 0xcd, 0xfa, 0xb4, 0x69, 0xef, 0x3a, 0x2f);
+
+// 02391f44-2767-4e6a-a484-9b47b506f3a4
+DEFINE_GUID(CLSID_VorbisDec,
+0x02391f44, 0x2767, 0x4e6a, 0xa4, 0x84, 0x9b, 0x47, 0xb5, 0x06, 0xf3, 0xa4);
+
+// 77983549-ffda-4a88-b48f-b924e8d1f01c
+DEFINE_GUID(CLSID_OggDSAboutPage,
+0x77983549, 0xffda, 0x4a88, 0xb4, 0x8f, 0xb9, 0x24, 0xe8, 0xd1, 0xf0, 0x1c);
+
+// {D2855FA9-61A7-4db0-B979-71F297C17A04}
+DEFINE_GUID(MEDIASUBTYPE_Ogg,
+0xd2855fa9, 0x61a7, 0x4db0, 0xb9, 0x79, 0x71, 0xf2, 0x97, 0xc1, 0x7a, 0x4);
+
+// cddca2d5-6d75-4f98-840e-737bedd5c63b
+DEFINE_GUID(MEDIASUBTYPE_Vorbis,
+0xcddca2d5, 0x6d75, 0x4f98, 0x84, 0x0e, 0x73, 0x7b, 0xed, 0xd5, 0xc6, 0x3b);
+
+// 6bddfa7e-9f22-46a9-ab5e-884eff294d9f
+DEFINE_GUID(FORMAT_VorbisFormat,
+0x6bddfa7e, 0x9f22, 0x46a9, 0xab, 0x5e, 0x88, 0x4e, 0xff, 0x29, 0x4d, 0x9f);
+
+typedef struct tagVORBISFORMAT
+{
+	WORD nChannels;
+	DWORD nSamplesPerSec;
+	DWORD nMinBitsPerSec;
+	DWORD nAvgBitsPerSec;
+	DWORD nMaxBitsPerSec;
+	float fQuality;
+} VORBISFORMAT, *PVORBISFORMAT, FAR *LPVORBISFORMAT;
+
+// {8D2FD10B-5841-4a6b-8905-588FEC1ADED9}
+DEFINE_GUID(MEDIASUBTYPE_Vorbis2, 
+0x8d2fd10b, 0x5841, 0x4a6b, 0x89, 0x5, 0x58, 0x8f, 0xec, 0x1a, 0xde, 0xd9);
+
+// {B36E107F-A938-4387-93C7-55E966757473}
+DEFINE_GUID(FORMAT_VorbisFormat2, 
+0xb36e107f, 0xa938, 0x4387, 0x93, 0xc7, 0x55, 0xe9, 0x66, 0x75, 0x74, 0x73);
+
+typedef struct tagVORBISFORMAT2
+{
+	DWORD Channels;
+	DWORD SamplesPerSec;
+	DWORD BitsPerSample;	
+	DWORD HeaderSize[3]; // 0: Identification, 1: Comment, 2: Setup
+} VORBISFORMAT2, *PVORBISFORMAT2, FAR *LPVORBISFORMAT2;
+
+//
+// Matroska
+//
+
+// {1AC0BEBD-4D2B-45ad-BCEB-F2C41C5E3788}
+DEFINE_GUID(MEDIASUBTYPE_Matroska, 
+0x1ac0bebd, 0x4d2b, 0x45ad, 0xbc, 0xeb, 0xf2, 0xc4, 0x1c, 0x5e, 0x37, 0x88);
+
+// {E487EB08-6B26-4be9-9DD3-993434D313FD}
+DEFINE_GUID(MEDIATYPE_Subtitle, 
+0xe487eb08, 0x6b26, 0x4be9, 0x9d, 0xd3, 0x99, 0x34, 0x34, 0xd3, 0x13, 0xfd);
+
+// {87C0B230-03A8-4fdf-8010-B27A5848200D}
+DEFINE_GUID(MEDIASUBTYPE_UTF8, 
+0x87c0b230, 0x3a8, 0x4fdf, 0x80, 0x10, 0xb2, 0x7a, 0x58, 0x48, 0x20, 0xd);
+
+// {3020560F-255A-4ddc-806E-6C5CC6DCD70A}
+DEFINE_GUID(MEDIASUBTYPE_SSA, 
+0x3020560f, 0x255a, 0x4ddc, 0x80, 0x6e, 0x6c, 0x5c, 0xc6, 0xdc, 0xd7, 0xa);
+
+// {326444F7-686F-47ff-A4B2-C8C96307B4C2}
+DEFINE_GUID(MEDIASUBTYPE_ASS, 
+0x326444f7, 0x686f, 0x47ff, 0xa4, 0xb2, 0xc8, 0xc9, 0x63, 0x7, 0xb4, 0xc2);
+
+// {370689E7-B226-4f67-978D-F10BC1A9C6AE}
+DEFINE_GUID(MEDIASUBTYPE_ASS2, 
+0x370689e7, 0xb226, 0x4f67, 0x97, 0x8d, 0xf1, 0xb, 0xc1, 0xa9, 0xc6, 0xae);
+
+// {76C421C4-DB89-42ec-936E-A9FBC1794714}
+DEFINE_GUID(MEDIASUBTYPE_SSF, 
+0x76c421c4, 0xdb89, 0x42ec, 0x93, 0x6e, 0xa9, 0xfb, 0xc1, 0x79, 0x47, 0x14);
+
+// {B753B29A-0A96-45be-985F-68351D9CAB90}
+DEFINE_GUID(MEDIASUBTYPE_USF, 
+0xb753b29a, 0xa96, 0x45be, 0x98, 0x5f, 0x68, 0x35, 0x1d, 0x9c, 0xab, 0x90);
+
+// {F7239E31-9599-4e43-8DD5-FBAF75CF37F1}
+DEFINE_GUID(MEDIASUBTYPE_VOBSUB, 
+0xf7239e31, 0x9599, 0x4e43, 0x8d, 0xd5, 0xfb, 0xaf, 0x75, 0xcf, 0x37, 0xf1);
+
+// {A33D2F7D-96BC-4337-B23B-A8B9FBC295E9}
+DEFINE_GUID(FORMAT_SubtitleInfo, 
+0xa33d2f7d, 0x96bc, 0x4337, 0xb2, 0x3b, 0xa8, 0xb9, 0xfb, 0xc2, 0x95, 0xe9);
+
+#pragma pack(push, 1)
+typedef struct {
+	DWORD dwOffset;	
+	CHAR IsoLang[4]; // three letter lang code + terminating zero
+	WCHAR TrackName[256]; // 256 chars ought to be enough for everyone :)
+} SUBTITLEINFO;
+#pragma pack(pop)
+
+// SUBTITLEINFO structure content starting at dwOffset (also the content of CodecPrivate)
+// --------------------------------------------------------------------------------------
+//
+// Here the text should start with the Byte Order Mark, even though 
+// UTF-8 is prefered, it also helps identifying the encoding type.
+//
+// MEDIASUBTYPE_USF: 
+//
+// <?xml version="1.0" encoding="UTF-8"?>
+// <!-- DOCTYPE USFSubtitles SYSTEM "USFV100.dtd" -->
+// <?xml-stylesheet type="text/xsl" href="USFV100.xsl"?>
+// 
+// <USFSubtitles version="1.0">
+// ... every element excluding <subtitles></subtitles> ...
+// </USFSubtitles>
+//
+// MEDIASUBTYPE_SSA/ASS:
+//
+// The file header and all sub-sections except [Events]
+//
+// MEDIATYPE_VOBSUB:
+//
+// TODO
+//
+
+// Data description of the media samples (everything is UTF-8 encoded here)
+// ------------------------------------------------------------------------
+//
+// MEDIASUBTYPE_USF:
+//
+// The text _inside_ the <subtitle>..</subtitle> element. 
+//
+// Since timing is set on the sample, there is no need to put
+// <subtitle start=".." stop=".." duration=".."> into the data.
+//
+// MEDIASUBTYPE_SSA/ASS:
+//
+// Comma separated values similar to the "Dialogue: ..." line with these fields:
+// ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+//
+// With the exception of ReadOrder every field can be found in ASS files. The
+// ReadOrder field is needed for the decoder to be able to reorder the streamed 
+// samples as they were placed originally in the file.
+//
+// If the source is only SSA, the Layer field can be left empty.
+//
+// MEDIATYPE_VOBSUB:
+//
+// Standard dvd subpic data, without the stream id at the beginning.
+//
+
+// Matroska CodecID mappings
+// ------------------------
+//
+// S_TEXT/ASCII	<->	MEDIATYPE_Text		MEDIASUBTYPE_NULL	FORMAT_None
+// S_TEXT/UTF8	<->	MEDIATYPE_Subtitle	MEDIASUBTYPE_UTF8	FORMAT_SubtitleInfo
+// S_TEXT/SSA	<->	MEDIATYPE_Subtitle	MEDIASUBTYPE_SSA	FORMAT_SubtitleInfo
+// S_TEXT/ASS	<->	MEDIATYPE_Subtitle	MEDIASUBTYPE_ASS	FORMAT_SubtitleInfo
+// S_TEXT/USF	<->	MEDIATYPE_Subtitle	MEDIASUBTYPE_USF	FORMAT_SubtitleInfo
+// S_VOBSUB		<-> MEDIATYPE_Subtitle	MEDIASUBTYPE_VOBSUB	FORMAT_SubtitleInfo
+// S_VOBSUB/ZLIB<-> MEDIATYPE_Subtitle	MEDIASUBTYPE_VOBSUB	FORMAT_SubtitleInfo
+//
+
diff --git a/vsfilter/include/unrar/UNRAR.H b/vsfilter/include/unrar/UNRAR.H
new file mode 100644
index 0000000000000000000000000000000000000000..2a839ac23a28e25811711606034f70d4fc2b0c62
--- /dev/null
+++ b/vsfilter/include/unrar/UNRAR.H
@@ -0,0 +1,129 @@
+#ifndef _UNRAR_DLL_
+#define _UNRAR_DLL_
+
+#define ERAR_END_ARCHIVE     10
+#define ERAR_NO_MEMORY       11
+#define ERAR_BAD_DATA        12
+#define ERAR_BAD_ARCHIVE     13
+#define ERAR_UNKNOWN_FORMAT  14
+#define ERAR_EOPEN           15
+#define ERAR_ECREATE         16
+#define ERAR_ECLOSE          17
+#define ERAR_EREAD           18
+#define ERAR_EWRITE          19
+#define ERAR_SMALL_BUF       20
+#define ERAR_UNKNOWN         21
+
+#define RAR_OM_LIST           0
+#define RAR_OM_EXTRACT        1
+
+#define RAR_SKIP              0
+#define RAR_TEST              1
+#define RAR_EXTRACT           2
+
+#define RAR_VOL_ASK           0
+#define RAR_VOL_NOTIFY        1
+
+#define RAR_DLL_VERSION       3
+
+struct RARHeaderData
+{
+  char         ArcName[260];
+  char         FileName[260];
+  unsigned int Flags;
+  unsigned int PackSize;
+  unsigned int UnpSize;
+  unsigned int HostOS;
+  unsigned int FileCRC;
+  unsigned int FileTime;
+  unsigned int UnpVer;
+  unsigned int Method;
+  unsigned int FileAttr;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+};
+
+
+struct RARHeaderDataEx
+{
+  char         ArcName[1024];
+  wchar_t      ArcNameW[1024];
+  char         FileName[1024];
+  wchar_t      FileNameW[1024];
+  unsigned int Flags;
+  unsigned int PackSize;
+  unsigned int PackSizeHigh;
+  unsigned int UnpSize;
+  unsigned int UnpSizeHigh;
+  unsigned int HostOS;
+  unsigned int FileCRC;
+  unsigned int FileTime;
+  unsigned int UnpVer;
+  unsigned int Method;
+  unsigned int FileAttr;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+  unsigned int Reserved[1024];
+};
+
+
+struct RAROpenArchiveData
+{
+  char         *ArcName;
+  unsigned int OpenMode;
+  unsigned int OpenResult;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+};
+
+struct RAROpenArchiveDataEx
+{
+  char         *ArcName;
+  wchar_t      *ArcNameW;
+  unsigned int OpenMode;
+  unsigned int OpenResult;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+  unsigned int Flags;
+  unsigned int Reserved[32];
+};
+
+enum UNRARCALLBACK_MESSAGES {
+  UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD
+};
+
+typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LONG UserData,LONG P1,LONG P2);
+
+typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
+typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef HANDLE (PASCAL * RAROpenArchive)(struct RAROpenArchiveData *ArchiveData);
+typedef HANDLE (PASCAL * RAROpenArchiveEx)(struct RAROpenArchiveDataEx *ArchiveData);
+typedef int (PASCAL * RARCloseArchive)(HANDLE hArcData);
+typedef int (PASCAL * RARReadHeader)(HANDLE hArcData,struct RARHeaderData *HeaderData);
+typedef int (PASCAL * RARReadHeaderEx)(HANDLE hArcData,struct RARHeaderDataEx *HeaderData);
+typedef int (PASCAL * RARProcessFile)(HANDLE hArcData,int Operation,char *DestPath,char *DestName);
+typedef void (PASCAL * RARSetCallback)(HANDLE hArcData,UNRARCALLBACK Callback,LONG UserData);
+typedef void (PASCAL * RARSetChangeVolProc)(HANDLE hArcData, CHANGEVOLPROC);
+typedef void (PASCAL * RARSetProcessDataProc)(HANDLE hArcData, PROCESSDATAPROC);
+typedef void (PASCAL * RARSetPassword)(HANDLE hArcData,char *Password);
+typedef int (PASCAL * RARGetDllVersion)();
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/vsfilter/include/unrar/UNRARDLL.TXT b/vsfilter/include/unrar/UNRARDLL.TXT
new file mode 100644
index 0000000000000000000000000000000000000000..86cc497c0e85110741f37d0798ab2d3726249ecd
--- /dev/null
+++ b/vsfilter/include/unrar/UNRARDLL.TXT
@@ -0,0 +1,566 @@
+
+    unRAR.dll Manual
+    ~~~~~~~~~~~~~~~~
+
+    UNRAR.DLL is a 32-bit Windows dynamic-link library which provides
+ file extraction from RAR archives.
+
+
+    Exported functions
+
+====================================================================
+HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData)
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Open RAR archive and allocate memory structures
+
+Parameters
+~~~~~~~~~~
+ArchiveData       Points to RAROpenArchiveData structure
+
+struct RAROpenArchiveData
+{
+  char *ArcName;
+  UINT OpenMode;
+  UINT OpenResult;
+  char *CmtBuf;
+  UINT CmtBufSize;
+  UINT CmtSize;
+  UINT CmtState;
+};
+
+Structure fields:
+
+ArcName
+  Input parameter which should point to zero terminated string 
+  containing the archive name. 
+
+OpenMode
+  Input parameter.
+
+  Possible values
+
+  RAR_OM_LIST           Open archive for reading file headers only
+  RAR_OM_EXTRACT        Open archive for testing and extracting files
+
+OpenResult
+  Output parameter.
+
+  Possible values
+
+  0                     Success
+  ERAR_NO_MEMORY        Not enough memory to initialize data structures
+  ERAR_BAD_DATA         Archive header broken
+  ERAR_BAD_ARCHIVE      File is not valid RAR archive
+  ERAR_EOPEN            File open error
+
+CmtBuf
+  Input parameter which should point to the buffer for archive 
+  comments. Maximum comment size is limited to 64Kb. Comment text is 
+  zero terminated. If the comment text is larger than the buffer 
+  size, the comment text will be truncated. If CmtBuf is set to 
+  NULL, comments will not be read. 
+
+CmtBufSize
+  Input parameter which should contain size of buffer for archive
+  comments.
+
+CmtSize
+  Output parameter containing size of comments actually read into the
+  buffer, cannot exceed CmtBufSize.
+
+CmtState
+  Output parameter.
+
+  Possible values
+
+  0                     comments not present
+  1                     Comments read completely
+  ERAR_NO_MEMORY        Not enough memory to extract comments
+  ERAR_BAD_DATA         Broken comment
+  ERAR_UNKNOWN_FORMAT   Unknown comment format
+  ERAR_SMALL_BUF        Buffer too small, comments not completely read
+
+Return values
+~~~~~~~~~~~~~
+  Archive handle or NULL in case of error
+
+
+========================================================================
+HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData)
+========================================================================
+
+Description
+~~~~~~~~~~~
+  Similar to RAROpenArchive, but uses RAROpenArchiveDataEx structure
+  allowing to specify Unicode archive name and returning information
+  about archive flags.
+
+Parameters
+~~~~~~~~~~
+ArchiveData       Points to RAROpenArchiveDataEx structure
+
+struct RAROpenArchiveDataEx
+{
+  char         *ArcName;
+  wchar_t      *ArcNameW;
+  unsigned int OpenMode;
+  unsigned int OpenResult;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+  unsigned int Flags;
+  unsigned int Reserved[32];
+};
+
+Structure fields:
+
+ArcNameW
+  Input parameter which should point to zero terminated Unicode string
+  containing the archive name or NULL if Unicode name is not specified.
+
+Flags
+  Output parameter. Combination of bit flags.
+
+  Possible values
+
+    0x0001  - Volume attribute (archive volume)
+    0x0002  - Archive comment present
+    0x0004  - Archive lock attribute
+    0x0008  - Solid attribute (solid archive)
+    0x0010  - New volume naming scheme ('volname.partN.rar')
+    0x0020  - Authenticity information present
+    0x0040  - Recovery record present
+    0x0080  - Block headers are encrypted
+    0x0100  - First volume (set only by RAR 3.0 and later)
+
+Reserved[32]
+  Reserved for future use. Must be zero.
+
+Information on other structure fields and function return values
+is available above, in RAROpenArchive function description.
+
+
+====================================================================
+int PASCAL RARCloseArchive(HANDLE hArcData)
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Close RAR archive and release allocated memory. It must be called when
+  archive processing is finished, even if the archive processing was stopped
+  due to an error.
+
+Parameters
+~~~~~~~~~~
+hArcData
+  This parameter should contain the archive handle obtained from the
+  RAROpenArchive function call.
+
+Return values
+~~~~~~~~~~~~~
+  0                     Success
+  ERAR_ECLOSE           Archive close error
+
+
+====================================================================
+int PASCAL RARReadHeader(HANDLE hArcData,
+                         struct RARHeaderData *HeaderData)
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Read header of file in archive.
+
+Parameters
+~~~~~~~~~~
+hArcData
+  This parameter should contain the archive handle obtained from the
+  RAROpenArchive function call.
+
+HeaderData
+  It should point to RARHeaderData structure:
+
+struct RARHeaderData
+{
+  char ArcName[260];
+  char FileName[260];
+  UINT Flags;
+  UINT PackSize;
+  UINT UnpSize;
+  UINT HostOS;
+  UINT FileCRC;
+  UINT FileTime;
+  UINT UnpVer;
+  UINT Method;
+  UINT FileAttr;
+  char *CmtBuf;
+  UINT CmtBufSize;
+  UINT CmtSize;
+  UINT CmtState;
+};
+
+Structure fields:
+
+ArcName
+  Output parameter which contains a zero terminated string of the
+  current archive name.  May be used to determine the current volume 
+  name. 
+
+FileName
+  Output parameter which contains a zero terminated string of the 
+  file name in OEM (DOS) encoding.
+
+Flags
+  Output parameter which contains file flags:
+
+  0x01 - file continued from previous volume
+  0x02 - file continued on next volume
+  0x04 - file encrypted with password
+  0x08 - file comment present
+  0x10 - compression of previous files is used (solid flag)
+
+  bits 7 6 5
+
+       0 0 0    - dictionary size   64 Kb
+       0 0 1    - dictionary size  128 Kb
+       0 1 0    - dictionary size  256 Kb
+       0 1 1    - dictionary size  512 Kb
+       1 0 0    - dictionary size 1024 Kb
+       1 0 1    - reserved
+       1 1 0    - reserved
+       1 1 1    - file is directory
+
+  Other bits are reserved.
+
+PackSize
+  Output parameter means packed file size or size of the
+  file part if file was split between volumes.
+
+UnpSize
+  Output parameter - unpacked file size.
+
+HostOS
+  Output parameter - operating system used for archiving:
+
+  0 - MS DOS;
+  1 - OS/2.
+  2 - Win32
+  3 - Unix
+
+FileCRC
+  Output parameter which contains unpacked file CRC. It should not be
+  used for file parts which were split between volumes.
+
+FileTime
+  Output parameter - contains date and time in standard MS DOS format.
+
+UnpVer
+  Output parameter - RAR version needed to extract file.
+  It is encoded as 10 * Major version + minor version.
+
+Method
+  Output parameter - packing method.
+
+FileAttr
+  Output parameter - file attributes.
+
+CmtBuf
+  File comments support is not implemented in the new DLL version yet.
+  Now CmtState is always 0.
+
+/*
+ * Input parameter which should point to the buffer for file
+ * comments. Maximum comment size is limited to 64Kb. Comment text is 
+ * a zero terminated string in OEM encoding. If the comment text is
+ * larger than the buffer size, the comment text will be truncated.
+ * If CmtBuf is set to NULL, comments will not be read. 
+ */
+
+CmtBufSize
+  Input parameter which should contain size of buffer for archive
+  comments.
+
+CmtSize
+  Output parameter containing size of comments actually read into the
+  buffer, should not exceed CmtBufSize.
+
+CmtState
+  Output parameter.
+
+  Possible values
+
+  0                     Absent comments
+  1                     Comments read completely
+  ERAR_NO_MEMORY        Not enough memory to extract comments
+  ERAR_BAD_DATA         Broken comment
+  ERAR_UNKNOWN_FORMAT   Unknown comment format
+  ERAR_SMALL_BUF        Buffer too small, comments not completely read
+
+Return values
+~~~~~~~~~~~~~
+
+  0                     Success
+  ERAR_END_ARCHIVE      End of archive
+  ERAR_BAD_DATA         File header broken
+
+
+====================================================================
+int PASCAL RARReadHeaderEx(HANDLE hArcData,
+                           struct RARHeaderDataEx *HeaderData)
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Similar to RARReadHeader, but uses RARHeaderDataEx structure,
+containing information about Unicode file names and 64 bit file sizes.
+
+struct RARHeaderDataEx
+{
+  char         ArcName[1024];
+  wchar_t      ArcNameW[1024];
+  char         FileName[1024];
+  wchar_t      FileNameW[1024];
+  unsigned int Flags;
+  unsigned int PackSize;
+  unsigned int PackSizeHigh;
+  unsigned int UnpSize;
+  unsigned int UnpSizeHigh;
+  unsigned int HostOS;
+  unsigned int FileCRC;
+  unsigned int FileTime;
+  unsigned int UnpVer;
+  unsigned int Method;
+  unsigned int FileAttr;
+  char         *CmtBuf;
+  unsigned int CmtBufSize;
+  unsigned int CmtSize;
+  unsigned int CmtState;
+  unsigned int Reserved[1024];
+};
+
+
+====================================================================
+int PASCAL RARProcessFile(HANDLE hArcData,
+                          int Operation,
+                          char *DestPath,
+                          char *DestName)
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Performs action and moves the current position in the archive to 
+  the next file. Extract or test the current file from the archive 
+  opened in RAR_OM_EXTRACT mode. If the mode RAR_OM_LIST is set, 
+  then a call to this function will simply skip the archive position 
+  to the next file. 
+
+Parameters
+~~~~~~~~~~
+hArcData
+  This parameter should contain the archive handle obtained from the
+  RAROpenArchive function call.
+
+Operation
+  File operation.
+
+  Possible values
+
+  RAR_SKIP              Move to the next file in the archive. If the 
+                        archive is solid and RAR_OM_EXTRACT mode was set 
+                        when the archive was opened, the current file will 
+                        be processed - the operation will be performed 
+                        slower than a simple seek. 
+
+  RAR_TEST              Test the current file and move to the next file in 
+                        the archive. If the archive was opened with 
+                        RAR_OM_LIST mode, the operation is equal to 
+                        RAR_SKIP. 
+
+  RAR_EXTRACT           Extract the current file and move to the next file.
+                        If the archive was opened with RAR_OM_LIST mode,
+                        the operation is equal to RAR_SKIP.
+
+
+DestPath
+  This parameter should point to a zero terminated string containing the 
+  destination directory to which to extract files to. If DestPath is equal 
+  to NULL it means extract to the current directory. This parameter has 
+  meaning only if DestName is NULL. 
+
+DestName
+  This parameter should point to a string containing the full path and name
+  of the file to be extracted or NULL as default. If DestName is defined
+  (not NULL) it overrides the original file name saved in the archive and 
+  DestPath setting. 
+
+  Both DestPath and DestName must be in OEM encoding. If necessary,
+  use CharToOem to convert text to OEM before passing to this function.
+
+Return values
+~~~~~~~~~~~~~
+  0                     Success
+  ERAR_BAD_DATA         File CRC error
+  ERAR_BAD_ARCHIVE      Volume is not valid RAR archive
+  ERAR_UNKNOWN_FORMAT   Unknown archive format
+  ERAR_EOPEN            Volume open error
+  ERAR_ECREATE          File create error
+  ERAR_ECLOSE           File close error
+  ERAR_EREAD            Read error
+  ERAR_EWRITE           Write error
+
+
+====================================================================
+void PASCAL RARSetCallback(HANDLE hArcData,
+            int PASCAL (*CallbackProc)(UINT msg,LONG UserData,LONG P1,LONG P2),
+            LONG UserData);
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Set a user-defined callback function to process Unrar events.
+
+Parameters
+~~~~~~~~~~
+hArcData
+  This parameter should contain the archive handle obtained from the
+  RAROpenArchive function call.
+
+CallbackProc
+  It should point to a user-defined callback function.
+
+  The function will be passed four parameters:
+
+
+  msg                    Type of event. Described below.
+
+  UserData               User defined value passed to RARSetCallback.
+
+  P1 and P2              Event dependent parameters. Described below.
+
+
+  Possible events
+
+    UCM_CHANGEVOLUME     Process volume change.
+
+      P1                   Points to the zero terminated name
+                           of the next volume.
+
+      P2                   The function call mode:
+
+        RAR_VOL_ASK          Required volume is absent. The function should
+                             prompt user and return a non-zero value
+                             to retry or return -1 value to terminate 
+                             operation. The function may also specify a new 
+                             volume name, placing it to the address specified
+                             by P1 parameter. 
+
+        RAR_VOL_NOTIFY       Required volume is successfully opened.
+                             This is a notification call and volume name
+                             modification is not allowed. The function should 
+                             return a non-zero value to continue or -1
+                             to terminate operation. 
+
+    UCM_PROCESSDATA          Process unpacked data. It may be used to read
+                             a file while it is being extracted or tested
+                             without actual extracting file to disk.
+                             Return a non-zero value to continue process
+                             or -1 to cancel the archive operation
+
+      P1                   Address pointing to the unpacked data.
+                           Function may refer to the data but must not
+                           change it.
+
+      P2                   Size of the unpacked data. It is guaranteed
+                           only that the size will not exceed the maximum
+                           dictionary size (4 Mb in RAR 3.0).
+
+    UCM_NEEDPASSWORD         DLL needs a password to process archive.
+                             This message must be processed if you wish
+                             to be able to handle archives with encrypted
+                             file names. It can be also used as replacement
+                             of RARSetPassword function even for usual
+                             encrypted files with non-encrypted names.
+
+      P1                   Address pointing to the buffer for a password.
+                           You need to copy a password here.
+
+      P2                   Size of the password buffer.
+
+
+UserData
+  User data passed to callback function.
+
+  Other functions of UNRAR.DLL should not be called from the callback
+  function.
+
+Return values
+~~~~~~~~~~~~~
+  None
+
+
+
+====================================================================
+void PASCAL RARSetChangeVolProc(HANDLE hArcData,
+            int PASCAL (*ChangeVolProc)(char *ArcName,int Mode));
+====================================================================
+
+Obsoleted, use RARSetCallback instead.
+
+
+
+====================================================================
+void PASCAL RARSetProcessDataProc(HANDLE hArcData,
+            int PASCAL (*ProcessDataProc)(unsigned char *Addr,int Size))
+====================================================================
+
+Obsoleted, use RARSetCallback instead.
+
+
+====================================================================
+void PASCAL RARSetPassword(HANDLE hArcData,
+                           char *Password);
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Set a password to decrypt files.
+
+Parameters
+~~~~~~~~~~
+hArcData
+  This parameter should contain the archive handle obtained from the
+  RAROpenArchive function call.
+
+Password
+  It should point to a string containing a zero terminated password.
+
+Return values
+~~~~~~~~~~~~~
+  None
+
+
+====================================================================
+void PASCAL RARGetDllVersion();
+====================================================================
+
+Description
+~~~~~~~~~~~
+  Returns unrar.dll version.
+
+Parameters
+~~~~~~~~~~
+  None.
+
+Return values
+~~~~~~~~~~~~~
+  Returns an integer value denoting DLL version. It is increased in case
+of changes in DLL API. The current version value is defined in unrar.h
+as RAR_DLL_VERSION
+
+  This function is absent in old versions of unrar.dll, so it may be wise
+to use LoadLibrary and GetProcAddress to access this function.
+
diff --git a/vsfilter/include/virtualdub/Filter.h b/vsfilter/include/virtualdub/Filter.h
new file mode 100644
index 0000000000000000000000000000000000000000..562b4efec3ac5a87d0be8ced8ab5b1c7c1ac96fc
--- /dev/null
+++ b/vsfilter/include/virtualdub/Filter.h
@@ -0,0 +1,167 @@
+#ifndef f_FILTER_H
+#define f_FILTER_H
+
+#include <windows.h>
+
+// This is really dumb, but necessary to support VTbls in C++.
+
+struct FilterVTbls {
+	void *pvtblVBitmap;
+};
+
+#ifdef VDEXT_MAIN
+struct FilterVTbls g_vtbls;
+#elif defined(VDEXT_NOTMAIN)
+extern struct FilterVTbls g_vtbls;
+#endif
+
+#define INITIALIZE_VTBLS		ff->InitVTables(&g_vtbls)
+
+#include "VBitmap.h"
+
+//////////////////
+
+struct CScriptObject;
+
+//////////////////
+
+enum {
+	FILTERPARAM_SWAP_BUFFERS	= 0x00000001L,
+	FILTERPARAM_NEEDS_LAST		= 0x00000002L,
+};
+
+///////////////////
+
+class VFBitmap;
+class FilterActivation;
+struct FilterFunctions;
+
+typedef int  (*FilterInitProc     )(FilterActivation *fa, const FilterFunctions *ff);
+typedef void (*FilterDeinitProc   )(FilterActivation *fa, const FilterFunctions *ff);
+typedef int  (*FilterRunProc      )(const FilterActivation *fa, const FilterFunctions *ff);
+typedef long (*FilterParamProc    )(FilterActivation *fa, const FilterFunctions *ff);
+typedef int  (*FilterConfigProc   )(FilterActivation *fa, const FilterFunctions *ff, HWND hWnd);
+typedef int  (*FilterConfig2Proc  )(FilterActivation *fa, const FilterFunctions *ff, HWND hWnd);
+typedef void (*FilterStringProc   )(const FilterActivation *fa, const FilterFunctions *ff, char *buf);
+typedef int  (*FilterStartProc    )(FilterActivation *fa, const FilterFunctions *ff);
+typedef int  (*FilterEndProc      )(FilterActivation *fa, const FilterFunctions *ff);
+typedef bool (*FilterScriptStrProc)(FilterActivation *fa, const FilterFunctions *, char *, int);
+
+typedef int (__cdecl *FilterModuleInitProc)(struct FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat);
+typedef void (__cdecl *FilterModuleDeinitProc)(struct FilterModule *fm, const FilterFunctions *ff);
+
+//////////
+
+typedef void (__cdecl *FilterPreviewButtonCallback)(bool fNewState, void *pData);
+typedef void (__cdecl *FilterPreviewSampleCallback)(VFBitmap *, long lFrame, long lCount, void *pData);
+
+class IFilterPreview {
+public:
+	virtual void SetButtonCallback(FilterPreviewButtonCallback, void *)=0;
+	virtual void SetSampleCallback(FilterPreviewSampleCallback, void *)=0;
+
+	virtual bool isPreviewEnabled()=0;
+	virtual void Toggle(HWND)=0;
+	virtual void Display(HWND, bool)=0;
+	virtual void RedoFrame()=0;
+	virtual void RedoSystem()=0;
+	virtual void UndoSystem()=0;
+	virtual void InitButton(HWND)=0;
+	virtual void Close()=0;
+	virtual bool SampleCurrentFrame()=0;
+	virtual long SampleFrames()=0;
+};
+
+//////////
+
+#define VIRTUALDUB_FILTERDEF_VERSION		(4)
+#define	VIRTUALDUB_FILTERDEF_COMPATIBLE		(4)
+
+// v3: added lCurrentSourceFrame to FrameStateInfo
+// v4: lots of additions (VirtualDub 1.2)
+
+typedef struct FilterModule {
+	struct FilterModule *next, *prev;
+	HINSTANCE				hInstModule;
+	FilterModuleInitProc	initProc;
+	FilterModuleDeinitProc	deinitProc;
+} FilterModule;
+
+typedef struct FilterDefinition {
+
+	struct FilterDefinition *next, *prev;
+	FilterModule *module;
+
+	char *				name;
+	char *				desc;
+	char *				maker;
+	void *				private_data;
+	int					inst_data_size;
+
+	FilterInitProc		initProc;
+	FilterDeinitProc	deinitProc;
+	FilterRunProc		runProc;
+	FilterParamProc		paramProc;
+	FilterConfigProc	configProc;
+	FilterStringProc	stringProc;
+	FilterStartProc		startProc;
+	FilterEndProc		endProc;
+
+	CScriptObject	*script_obj;
+
+	FilterScriptStrProc	fssProc;
+
+} FilterDefinition;
+
+//////////
+
+// FilterStateInfo: contains dynamic info about file being processed
+
+class FilterStateInfo {
+public:
+	long	lCurrentFrame;				// current output frame
+	long	lMicrosecsPerFrame;			// microseconds per output frame
+	long	lCurrentSourceFrame;		// current source frame
+	long	lMicrosecsPerSrcFrame;		// microseconds per source frame
+	long	lSourceFrameMS;				// source frame timestamp
+	long	lDestFrameMS;				// output frame timestamp
+};
+
+// VFBitmap: VBitmap extended to hold filter-specific information
+
+class VFBitmap : public VBitmap {
+public:
+	enum {
+		NEEDS_HDC		= 0x00000001L,
+	};
+
+	DWORD	dwFlags;
+	HDC		hdc;
+};
+
+// FilterActivation: This is what is actually passed to filters at runtime.
+
+class FilterActivation {
+public:
+	FilterDefinition *filter;
+	void *filter_data;
+	VFBitmap &dst, &src;
+	VFBitmap *rsrc, *last;
+	unsigned long x1, y1, x2, y2;
+
+	FilterStateInfo *pfsi;
+	IFilterPreview *ifp;
+
+	FilterActivation(VFBitmap& _dst, VFBitmap& _src) : dst(_dst), src(_src) {}
+	FilterActivation(const FilterActivation& fa, VFBitmap& _dst, VFBitmap& _src);
+};
+
+struct FilterFunctions {
+	FilterDefinition *(*addFilter)(FilterModule *, FilterDefinition *, int fd_len);
+	void (*removeFilter)(FilterDefinition *);
+	bool (*isFPUEnabled)();
+	bool (*isMMXEnabled)();
+	void (*InitVTables)(struct FilterVTbls *);
+};
+
+#endif
diff --git a/vsfilter/include/virtualdub/ScriptError.h b/vsfilter/include/virtualdub/ScriptError.h
new file mode 100644
index 0000000000000000000000000000000000000000..4f9edf0f98e1e9c9746683d0d61ad9044dfe578e
--- /dev/null
+++ b/vsfilter/include/virtualdub/ScriptError.h
@@ -0,0 +1,55 @@
+#ifndef f_SYLIA_SCRIPTERROR_H
+#define f_SYLIA_SCRIPTERROR_H
+
+class CScriptError {
+public:
+	int err;
+
+	enum {
+		PARSE_ERROR=1,
+		SEMICOLON_EXPECTED,
+		IDENTIFIER_EXPECTED,
+
+		TYPE_INT_REQUIRED,
+		TYPE_ARRAY_REQUIRED,
+		TYPE_FUNCTION_REQUIRED,
+		TYPE_OBJECT_REQUIRED,
+
+		OBJECT_MEMBER_NAME_REQUIRED,
+		FUNCCALLEND_EXPECTED,
+		TOO_MANY_PARAMS,
+		DIVIDE_BY_ZERO,
+		VAR_NOT_FOUND,
+		MEMBER_NOT_FOUND,
+		OVERLOADED_FUNCTION_NOT_FOUND,
+		IDENT_TOO_LONG,
+		OPERATOR_EXPECTED,
+		CLOSEPARENS_EXPECTED,
+		CLOSEBRACKET_EXPECTED,
+
+		VAR_UNDEFINED,
+
+		OUT_OF_STRING_SPACE,
+		OUT_OF_MEMORY,
+		INTERNAL_ERROR,
+		EXTERNAL_ERROR,
+
+		FCALL_OUT_OF_RANGE,
+		FCALL_INVALID_PTYPE,
+		FCALL_UNKNOWN_STR,
+	};
+
+	CScriptError(int err_num) : err(err_num) { }
+
+	int getErr() { return err; }
+};
+
+#define SCRIPT_ERROR(x)			throw CScriptError(CScriptError::##x)
+
+extern "C" __declspec(dllexport) char * __stdcall TranslateScriptError(int);
+
+char inline *TranslateScriptError(CScriptError cse) {
+	return TranslateScriptError(cse.getErr());
+}
+
+#endif
diff --git a/vsfilter/include/virtualdub/ScriptInterpreter.h b/vsfilter/include/virtualdub/ScriptInterpreter.h
new file mode 100644
index 0000000000000000000000000000000000000000..b41920f6fb0602c1311a696abb187b6fe5bbf121
--- /dev/null
+++ b/vsfilter/include/virtualdub/ScriptInterpreter.h
@@ -0,0 +1,32 @@
+#ifndef f_SYLIA_SCRIPTINTERPRETER_H
+#define f_SYLIA_SCRIPTINTERPRETER_H
+
+class CScriptValue;
+class CScriptError;
+struct CScriptObject;
+class IScriptInterpreter;
+
+typedef CScriptValue (*ScriptRootHandlerPtr)(IScriptInterpreter *,char *,void *);
+
+class IScriptInterpreter {
+public:
+	virtual	void Destroy()										=0;
+
+	virtual void SetRootHandler(ScriptRootHandlerPtr, void *)	=0;
+
+	virtual void ExecuteLine(char *s)							=0;
+
+	virtual void ScriptError(int e)								=0;
+	virtual char* TranslateScriptError(CScriptError& cse)		=0;
+	virtual char** AllocTempString(long l)						=0;
+
+	virtual CScriptValue LookupObjectMember(CScriptObject *obj, void *, char *szIdent) = 0;
+};
+
+extern "C" __declspec(dllexport) IScriptInterpreter * __stdcall CreateScriptInterpreter();
+
+#define GETPROC_CREATESCRIPTINTERPRETER(hInst)	((IScriptInterpreter *(__stdcall *)())GetProcAddress(hInst, "_CreateScriptInterpreter@0"))
+
+#define EXT_SCRIPT_ERROR(x)	(isi->ScriptError((CScriptError::x)))
+
+#endif
diff --git a/vsfilter/include/virtualdub/ScriptValue.h b/vsfilter/include/virtualdub/ScriptValue.h
new file mode 100644
index 0000000000000000000000000000000000000000..14b79785c6b1f2a30c009c4c8d9a18caefc06217
--- /dev/null
+++ b/vsfilter/include/virtualdub/ScriptValue.h
@@ -0,0 +1,88 @@
+#ifndef f_SYLIA_SCRIPTVALUE_H
+#define f_SYLIA_SCRIPTVALUE_H
+
+class CScriptArray;
+struct CScriptObject;
+class CScriptValue;
+class IScriptInterpreter;
+class VariableTableEntry;
+
+typedef CScriptValue (*ScriptObjectLookupFuncPtr)(IScriptInterpreter *, CScriptObject *, void *lpVoid, char *szName);
+typedef CScriptValue (*ScriptFunctionPtr)(IScriptInterpreter *, void *, CScriptValue *, int);
+typedef void (*ScriptVoidFunctionPtr)(IScriptInterpreter *, void *, CScriptValue *, int);
+typedef int (*ScriptIntFunctionPtr)(IScriptInterpreter *, void *, CScriptValue *, int);
+typedef CScriptValue (*ScriptArrayFunctionPtr)(IScriptInterpreter *, void *, int);
+
+typedef struct ScriptFunctionDef {
+	ScriptFunctionPtr func_ptr;
+	char *name;
+	char *arg_list;
+} ScriptFunctionDef;
+
+typedef struct ScriptObjectDef {
+	char *name;
+	CScriptObject *obj;
+} ScriptObjectDef;
+
+typedef struct CScriptObject {
+	ScriptObjectLookupFuncPtr Lookup;
+	ScriptFunctionDef		*func_list;
+	ScriptObjectDef			*obj_list;
+} CScriptObject;
+
+class CScriptValue {
+public:
+	enum { T_VOID, T_INT, T_PINT, T_STR, T_ARRAY, T_OBJECT, T_FNAME, T_FUNCTION, T_VARLV } type;
+	CScriptObject *thisPtr;
+	union {
+		int i;
+		int *pi;
+		char **s;
+		ScriptArrayFunctionPtr ary;
+		CScriptObject *obj;
+		ScriptFunctionPtr func;
+		ScriptFunctionDef *fname;
+		VariableTableEntry *vte;
+	} u;
+	void *lpVoid;
+
+	CScriptValue()						{ type = T_VOID; }
+	CScriptValue(int i)					{ type = T_INT;			u.i = i; }
+	CScriptValue(int *pi)				{ type = T_PINT;		u.pi = pi; }
+	CScriptValue(char **s)				{ type = T_STR;			u.s = s; }
+	CScriptValue(CScriptObject *obj)	{ type = T_OBJECT;		u.obj = obj; }
+	CScriptValue(CScriptObject *_thisPtr, ScriptArrayFunctionPtr csa) {
+		type = T_ARRAY;
+		u.ary = csa;
+		thisPtr = _thisPtr;
+	}
+	CScriptValue(CScriptObject *_thisPtr, ScriptFunctionDef *sfd)	{
+		type = T_FNAME;
+		u.fname = sfd;
+		thisPtr = _thisPtr;
+	}
+	CScriptValue(CScriptObject *_thisPtr, ScriptFunctionPtr fn)	{
+		type = T_FUNCTION;
+		u.func = fn;
+		thisPtr = _thisPtr;
+	}
+	CScriptValue(VariableTableEntry *vte) { type = T_VARLV;		u.vte = vte; }
+
+	bool isVoid()			{ return type == T_VOID; }
+	bool isInt()			{ return type == T_INT; }
+	bool isString()			{ return type == T_STR; }
+	bool isArray()			{ return type == T_ARRAY; }
+	bool isObject()			{ return type == T_OBJECT; }
+	bool isFName()			{ return type == T_FNAME; }
+	bool isFunction()		{ return type == T_FUNCTION; }
+	bool isVarLV()			{ return type == T_VARLV; }
+
+	int					asInt()			{ return u.i; }
+	char **				asString()		{ return u.s; }
+	ScriptArrayFunctionPtr		asArray()		{ return u.ary; }
+	CScriptObject *		asObject()		{ return u.obj; }
+	ScriptFunctionPtr	asFunction()	{ return u.func; }
+	VariableTableEntry*	asVarLV()		{ return u.vte; }
+};
+
+#endif
diff --git a/vsfilter/include/virtualdub/VBitmap.h b/vsfilter/include/virtualdub/VBitmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..89964447a46528b731e76981b8627332e8457c38
--- /dev/null
+++ b/vsfilter/include/virtualdub/VBitmap.h
@@ -0,0 +1,113 @@
+#ifndef f_VIRTUALDUB_VBITMAP_H
+#define f_VIRTUALDUB_VBITMAP_H
+
+#include <windows.h>
+
+typedef unsigned long	Pixel;
+typedef unsigned long	Pixel32;
+typedef unsigned char	Pixel8;
+typedef long			PixCoord;
+typedef	long			PixDim;
+typedef	long			PixOffset;
+
+#ifdef VDEXT_VIDEO_FILTER
+#define NOVTABLE __declspec(novtable)
+#else
+#define NOVTABLE
+#endif
+
+class NOVTABLE VBitmap {
+public:
+	Pixel *			data;
+	Pixel *			palette;
+	int				depth;
+	PixCoord		w, h;
+	PixOffset		pitch;
+	PixOffset		modulo;
+	PixOffset		size;
+	PixOffset		offset;
+
+	Pixel *Address(PixCoord x, PixCoord y) const {
+		return Addressi(x, h-y-1);
+	}
+
+	Pixel *Addressi(PixCoord x, PixCoord y) const {
+		return (Pixel *)((char *)data + y*pitch + x*(depth>>3));
+	}
+
+	Pixel *Address32(PixCoord x, PixCoord y) const {
+		return Address32i(x, h-y-1);
+	}
+
+	Pixel *Address32i(PixCoord x, PixCoord y) const {
+		return (Pixel *)((char *)data + y*pitch + x*sizeof(Pixel));
+	}
+
+	PixOffset PitchAlign4() {
+		return ((w * depth + 31)/32)*4;
+	}
+
+	PixOffset PitchAlign8() {
+		return ((w * depth + 63)/64)*8;
+	}
+
+	PixOffset Modulo() {
+		return pitch - (w*depth+7)/8;
+	}
+
+	PixOffset Size() {
+		return pitch*h;
+	}
+
+	//////
+
+	VBitmap() throw() {
+#ifdef VDEXT_VIDEO_FILTER
+		init();
+#endif
+	}
+	VBitmap(void *data, PixDim w, PixDim h, int depth) throw();
+	VBitmap(void *data, BITMAPINFOHEADER *) throw();
+
+#ifdef VDEXT_VIDEO_FILTER
+	void init() throw() { *(void **)this = g_vtbls.pvtblVBitmap; }
+#endif
+
+	virtual VBitmap& init(void *data, PixDim w, PixDim h, int depth) throw();
+	virtual VBitmap& init(void *data, BITMAPINFOHEADER *) throw();
+
+	virtual void MakeBitmapHeader(BITMAPINFOHEADER *bih) const throw();
+
+	virtual void AlignTo4() throw();
+	virtual void AlignTo8() throw();
+
+	virtual void BitBlt(PixCoord x2, PixCoord y2, const VBitmap *src, PixCoord x1, PixCoord y1, PixDim dx, PixDim dy) const throw();
+	virtual void BitBltDither(PixCoord x2, PixCoord y2, const VBitmap *src, PixDim x1, PixDim y1, PixDim dx, PixDim dy, bool to565) const throw();
+	virtual void BitBlt565(PixCoord x2, PixCoord y2, const VBitmap *src, PixDim x1, PixDim y1, PixDim dx, PixDim dy) const throw();
+
+	virtual bool BitBltXlat1(PixCoord x2, PixCoord y2, const VBitmap *src, PixCoord x1, PixCoord y1, PixDim dx, PixDim dy, const Pixel8 *tbl) const throw();
+	virtual bool BitBltXlat3(PixCoord x2, PixCoord y2, const VBitmap *src, PixCoord x1, PixCoord y1, PixDim dx, PixDim dy, const Pixel32 *tbl) const throw();
+
+	virtual bool StretchBltNearestFast(PixCoord x1, PixCoord y1, PixDim dx, PixDim dy, const VBitmap *src, double x2, double y2, double dx1, double dy1) const throw();
+
+	virtual bool StretchBltBilinearFast(PixCoord x1, PixCoord y1, PixDim dx, PixDim dy, const VBitmap *src, double x2, double y2, double dx1, double dy1) const throw();
+
+	virtual bool RectFill(PixCoord x1, PixCoord y1, PixDim dx, PixDim dy, Pixel32 c) const throw();
+
+	enum {
+		HISTO_LUMA,
+		HISTO_GRAY,
+		HISTO_RED,
+		HISTO_GREEN,
+		HISTO_BLUE,
+	};
+
+	virtual bool Histogram(PixCoord x, PixCoord y, PixCoord dx, PixCoord dy, long *pHisto, int iHistoType) const throw();
+
+private:
+	bool dualrectclip(PixCoord& x2, PixCoord& y2, const VBitmap *src, PixCoord& x1, PixCoord& y1, PixDim& dx, PixDim& dy) const throw();
+};
+
+#undef NOVTABLE
+
+#endif
diff --git a/vsfilter/include/virtualdub/VirtualDub.h b/vsfilter/include/virtualdub/VirtualDub.h
new file mode 100644
index 0000000000000000000000000000000000000000..5195b15f55d51e244a94a1d33f94b7332f705aee
--- /dev/null
+++ b/vsfilter/include/virtualdub/VirtualDub.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "Filter.h"
+#include "ScriptInterpreter.h"
+#include "ScriptError.h"
+#include "ScriptValue.h"
+#include "VBitmap.h"
diff --git a/vsfilter/include/winddk/devioctl.h b/vsfilter/include/winddk/devioctl.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a0b65c147c30151bf9cfbe477791d652d78dc95
--- /dev/null
+++ b/vsfilter/include/winddk/devioctl.h
@@ -0,0 +1,145 @@
+/*++ BUILD Version: 0004    // Increment this if a change has global effects
+
+Copyright (c) 1992-1999  Microsoft Corporation
+
+Module Name:
+
+    devioctl.h
+
+Abstract:
+
+    This module contains
+
+Author:
+
+    Andre Vachon (andreva) 21-Feb-1992
+
+
+Revision History:
+
+
+--*/
+
+// begin_winioctl
+
+#ifndef _DEVIOCTL_
+#define _DEVIOCTL_
+
+// begin_ntddk begin_wdm begin_nthal begin_ntifs
+//
+// Define the various device type values.  Note that values used by Microsoft
+// Corporation are in the range 0-32767, and 32768-65535 are reserved for use
+// by customers.
+//
+
+#define DEVICE_TYPE ULONG
+
+#define FILE_DEVICE_BEEP                0x00000001
+#define FILE_DEVICE_CD_ROM              0x00000002
+#define FILE_DEVICE_CD_ROM_FILE_SYSTEM  0x00000003
+#define FILE_DEVICE_CONTROLLER          0x00000004
+#define FILE_DEVICE_DATALINK            0x00000005
+#define FILE_DEVICE_DFS                 0x00000006
+#define FILE_DEVICE_DISK                0x00000007
+#define FILE_DEVICE_DISK_FILE_SYSTEM    0x00000008
+#define FILE_DEVICE_FILE_SYSTEM         0x00000009
+#define FILE_DEVICE_INPORT_PORT         0x0000000a
+#define FILE_DEVICE_KEYBOARD            0x0000000b
+#define FILE_DEVICE_MAILSLOT            0x0000000c
+#define FILE_DEVICE_MIDI_IN             0x0000000d
+#define FILE_DEVICE_MIDI_OUT            0x0000000e
+#define FILE_DEVICE_MOUSE               0x0000000f
+#define FILE_DEVICE_MULTI_UNC_PROVIDER  0x00000010
+#define FILE_DEVICE_NAMED_PIPE          0x00000011
+#define FILE_DEVICE_NETWORK             0x00000012
+#define FILE_DEVICE_NETWORK_BROWSER     0x00000013
+#define FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014
+#define FILE_DEVICE_NULL                0x00000015
+#define FILE_DEVICE_PARALLEL_PORT       0x00000016
+#define FILE_DEVICE_PHYSICAL_NETCARD    0x00000017
+#define FILE_DEVICE_PRINTER             0x00000018
+#define FILE_DEVICE_SCANNER             0x00000019
+#define FILE_DEVICE_SERIAL_MOUSE_PORT   0x0000001a
+#define FILE_DEVICE_SERIAL_PORT         0x0000001b
+#define FILE_DEVICE_SCREEN              0x0000001c
+#define FILE_DEVICE_SOUND               0x0000001d
+#define FILE_DEVICE_STREAMS             0x0000001e
+#define FILE_DEVICE_TAPE                0x0000001f
+#define FILE_DEVICE_TAPE_FILE_SYSTEM    0x00000020
+#define FILE_DEVICE_TRANSPORT           0x00000021
+#define FILE_DEVICE_UNKNOWN             0x00000022
+#define FILE_DEVICE_VIDEO               0x00000023
+#define FILE_DEVICE_VIRTUAL_DISK        0x00000024
+#define FILE_DEVICE_WAVE_IN             0x00000025
+#define FILE_DEVICE_WAVE_OUT            0x00000026
+#define FILE_DEVICE_8042_PORT           0x00000027
+#define FILE_DEVICE_NETWORK_REDIRECTOR  0x00000028
+#define FILE_DEVICE_BATTERY             0x00000029
+#define FILE_DEVICE_BUS_EXTENDER        0x0000002a
+#define FILE_DEVICE_MODEM               0x0000002b
+#define FILE_DEVICE_VDM                 0x0000002c
+#define FILE_DEVICE_MASS_STORAGE        0x0000002d
+#define FILE_DEVICE_SMB                 0x0000002e
+#define FILE_DEVICE_KS                  0x0000002f
+#define FILE_DEVICE_CHANGER             0x00000030
+#define FILE_DEVICE_SMARTCARD           0x00000031
+#define FILE_DEVICE_ACPI                0x00000032
+#define FILE_DEVICE_DVD                 0x00000033
+#define FILE_DEVICE_FULLSCREEN_VIDEO    0x00000034
+#define FILE_DEVICE_DFS_FILE_SYSTEM     0x00000035
+#define FILE_DEVICE_DFS_VOLUME          0x00000036
+#define FILE_DEVICE_SERENUM             0x00000037
+#define FILE_DEVICE_TERMSRV             0x00000038
+#define FILE_DEVICE_KSEC                0x00000039
+#define FILE_DEVICE_FIPS		0x0000003A
+
+//
+// Macro definition for defining IOCTL and FSCTL function control codes.  Note
+// that function codes 0-2047 are reserved for Microsoft Corporation, and
+// 2048-4095 are reserved for customers.
+//
+
+#define CTL_CODE( DeviceType, Function, Method, Access ) (                 \
+    ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+)
+
+//
+// Macro to extract device type out of the device io control code
+//
+#define DEVICE_TYPE_FROM_CTL_CODE(ctrlCode)     (((ULONG)(ctrlCode & 0xffff0000)) >> 16)
+
+//
+// Define the method codes for how buffers are passed for I/O and FS controls
+//
+
+#define METHOD_BUFFERED                 0
+#define METHOD_IN_DIRECT                1
+#define METHOD_OUT_DIRECT               2
+#define METHOD_NEITHER                  3
+
+//
+// Define the access check value for any access
+//
+//
+// The FILE_READ_ACCESS and FILE_WRITE_ACCESS constants are also defined in
+// ntioapi.h as FILE_READ_DATA and FILE_WRITE_DATA. The values for these
+// constants *MUST* always be in sync.
+//
+//
+// FILE_SPECIAL_ACCESS is checked by the NT I/O system the same as FILE_ANY_ACCESS.
+// The file systems, however, may add additional access checks for I/O and FS controls
+// that use this value.
+//
+
+
+#define FILE_ANY_ACCESS                 0
+#define FILE_SPECIAL_ACCESS    (FILE_ANY_ACCESS)
+#define FILE_READ_ACCESS          ( 0x0001 )    // file & pipe
+#define FILE_WRITE_ACCESS         ( 0x0002 )    // file & pipe
+
+// end_ntddk end_wdm end_nthal end_ntifs
+
+#endif // _DEVIOCTL_
+
+// end_winioctl
+
diff --git a/vsfilter/include/winddk/ntddcdrm.h b/vsfilter/include/winddk/ntddcdrm.h
new file mode 100644
index 0000000000000000000000000000000000000000..6590a4eed1ab1b8e79ac4fb109fb7fd22b7996ea
--- /dev/null
+++ b/vsfilter/include/winddk/ntddcdrm.h
@@ -0,0 +1,548 @@
+/*++ BUILD Version: 0001    // Increment this if a change has global effects
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    ntddcdrm.h
+
+Abstract:
+
+    This module contains structures and definitions
+    associated with CDROM IOCTls.
+
+Author:
+
+    Mike Glass
+
+Revision History:
+
+--*/
+
+// begin_winioctl
+
+#ifndef _NTDDCDRM_
+#define _NTDDCDRM_
+
+#if _MSC_VER >= 1200
+#pragma warning(push)
+#endif
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+//
+// remove some level 4 warnings for this header file:
+#pragma warning(disable:4200) // array[0]
+#pragma warning(disable:4201) // nameless struct/unions
+#pragma warning(disable:4214) // bit fields other than int
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// NtDeviceIoControlFile IoControlCode values for this device.
+//
+// Warning:  Remember that the low two bits of the code specify how the
+//           buffers are passed to the driver!
+//
+
+#define IOCTL_CDROM_BASE                 FILE_DEVICE_CD_ROM
+
+#define IOCTL_CDROM_UNLOAD_DRIVER        CTL_CODE(IOCTL_CDROM_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+//
+// CDROM Audio Device Control Functions
+//
+
+#define IOCTL_CDROM_READ_TOC         CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_SEEK_AUDIO_MSF   CTL_CODE(IOCTL_CDROM_BASE, 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_STOP_AUDIO       CTL_CODE(IOCTL_CDROM_BASE, 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_PAUSE_AUDIO      CTL_CODE(IOCTL_CDROM_BASE, 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_RESUME_AUDIO     CTL_CODE(IOCTL_CDROM_BASE, 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_GET_VOLUME       CTL_CODE(IOCTL_CDROM_BASE, 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_PLAY_AUDIO_MSF   CTL_CODE(IOCTL_CDROM_BASE, 0x0006, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_SET_VOLUME       CTL_CODE(IOCTL_CDROM_BASE, 0x000A, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_READ_Q_CHANNEL   CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_GET_CONTROL      CTL_CODE(IOCTL_CDROM_BASE, 0x000D, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_GET_LAST_SESSION CTL_CODE(IOCTL_CDROM_BASE, 0x000E, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_RAW_READ         CTL_CODE(IOCTL_CDROM_BASE, 0x000F, METHOD_OUT_DIRECT,  FILE_READ_ACCESS)
+#define IOCTL_CDROM_DISK_TYPE        CTL_CODE(IOCTL_CDROM_BASE, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_CDROM_GET_DRIVE_GEOMETRY    CTL_CODE(IOCTL_CDROM_BASE, 0x0013, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX CTL_CODE(IOCTL_CDROM_BASE, 0x0014, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_CDROM_READ_TOC_EX       CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_GET_CONFIGURATION CTL_CODE(IOCTL_CDROM_BASE, 0x0016, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+// end_winioctl
+
+//
+// The following device control codes are common for all class drivers.  The
+// functions codes defined here must match all of the other class drivers.
+//
+// Warning: these codes will be replaced in the future with the IOCTL_STORAGE
+// codes included below
+//
+
+#define IOCTL_CDROM_CHECK_VERIFY    CTL_CODE(IOCTL_CDROM_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_MEDIA_REMOVAL   CTL_CODE(IOCTL_CDROM_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_EJECT_MEDIA     CTL_CODE(IOCTL_CDROM_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_LOAD_MEDIA      CTL_CODE(IOCTL_CDROM_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_RESERVE         CTL_CODE(IOCTL_CDROM_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_RELEASE         CTL_CODE(IOCTL_CDROM_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_CDROM_FIND_NEW_DEVICES CTL_CODE(IOCTL_CDROM_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+//
+// The following file contains the IOCTL_STORAGE class ioctl definitions
+//
+
+#include "ntddstor.h"
+
+// begin_winioctl
+
+//
+// The following device control code is for the SIMBAD simulated bad
+// sector facility. See SIMBAD.H in this directory for related structures.
+//
+
+#define IOCTL_CDROM_SIMBAD        CTL_CODE(IOCTL_CDROM_BASE, 0x1003, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+//
+// Maximum CD Rom size
+//
+
+#define MAXIMUM_NUMBER_TRACKS 100
+#define MAXIMUM_CDROM_SIZE 804
+#define MINIMUM_CDROM_READ_TOC_EX_SIZE 2  // two bytes min transferred
+
+//
+// READ_TOC_EX structure
+//
+typedef struct _CDROM_READ_TOC_EX {
+    UCHAR Format    : 4;
+    UCHAR Reserved1 : 3; // future expansion
+    UCHAR Msf       : 1;
+    UCHAR SessionTrack;
+    UCHAR Reserved2;     // future expansion
+    UCHAR Reserved3;     // future expansion
+} CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
+
+#define CDROM_READ_TOC_EX_FORMAT_TOC      0x00
+#define CDROM_READ_TOC_EX_FORMAT_SESSION  0x01
+#define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02
+#define CDROM_READ_TOC_EX_FORMAT_PMA      0x03
+#define CDROM_READ_TOC_EX_FORMAT_ATIP     0x04
+#define CDROM_READ_TOC_EX_FORMAT_CDTEXT   0x05
+
+//
+// CD ROM Table OF Contents (TOC)
+// Format 0 - Get table of contents
+//
+
+typedef struct _TRACK_DATA {
+    UCHAR Reserved;
+    UCHAR Control : 4;
+    UCHAR Adr : 4;
+    UCHAR TrackNumber;
+    UCHAR Reserved1;
+    UCHAR Address[4];
+} TRACK_DATA, *PTRACK_DATA;
+
+typedef struct _CDROM_TOC {
+
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR FirstTrack;
+    UCHAR LastTrack;
+
+    //
+    // Track data
+    //
+
+    TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS];
+} CDROM_TOC, *PCDROM_TOC;
+
+#define CDROM_TOC_SIZE sizeof(CDROM_TOC)
+
+//
+// CD ROM Table OF Contents
+// Format 1 - Session Information
+//
+
+typedef struct _CDROM_TOC_SESSION_DATA {
+    
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR FirstCompleteSession;
+    UCHAR LastCompleteSession;
+
+    //
+    // One track, representing the first track
+    // of the last finished session
+    //
+
+    TRACK_DATA TrackData[1];
+
+} CDROM_TOC_SESSION_DATA, *PCDROM_TOC_SESSION_DATA;
+
+
+//
+// CD ROM Table OF Contents
+// Format 2 - Full TOC
+//
+
+typedef struct _CDROM_TOC_FULL_TOC_DATA_BLOCK {
+    UCHAR SessionNumber;
+    UCHAR Control      : 4;
+    UCHAR Adr          : 4;
+    UCHAR Reserved1;
+    UCHAR Point;
+    UCHAR MsfExtra[3];
+    UCHAR Zero;
+    UCHAR Msf[3];
+} CDROM_TOC_FULL_TOC_DATA_BLOCK, *PCDROM_TOC_FULL_TOC_DATA_BLOCK;
+
+typedef struct _CDROM_TOC_FULL_TOC_DATA {
+    
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR FirstCompleteSession;
+    UCHAR LastCompleteSession;
+
+    //
+    // one to N descriptors included
+    //
+
+    CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0];
+
+} CDROM_TOC_FULL_TOC_DATA, *PCDROM_TOC_FULL_TOC_DATA;
+
+//
+// CD ROM Table OF Contents
+// Format 3 - Program Memory Area
+//
+typedef struct _CDROM_TOC_PMA_DATA {
+    
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR Reserved1;
+    UCHAR Reserved2;
+
+    //
+    // one to N descriptors included
+    //
+
+    CDROM_TOC_FULL_TOC_DATA_BLOCK Descriptors[0];
+
+} CDROM_TOC_PMA_DATA, *PCDROM_TOC_PMA_DATA;
+
+//
+// CD ROM Table OF Contents
+// Format 4 - Absolute Time In Pregroove
+//
+
+typedef struct _CDROM_TOC_ATIP_DATA_BLOCK {
+
+    UCHAR CdrwReferenceSpeed : 3;
+    UCHAR Reserved3          : 1;
+    UCHAR WritePower         : 3;
+    UCHAR True1              : 1;
+    UCHAR Reserved4       : 6;
+    UCHAR UnrestrictedUse : 1;
+    UCHAR Reserved5       : 1;
+    UCHAR A3Valid     : 1;
+    UCHAR A2Valid     : 1;
+    UCHAR A1Valid     : 1;
+    UCHAR DiscSubType : 3;
+    UCHAR IsCdrw      : 1;
+    UCHAR True2       : 1;
+    UCHAR Reserved7;
+    
+    UCHAR LeadInMsf[3];
+    UCHAR Reserved8;
+    
+    UCHAR LeadOutMsf[3];
+    UCHAR Reserved9;
+    
+    UCHAR A1Values[3];
+    UCHAR Reserved10;
+    
+    UCHAR A2Values[3];
+    UCHAR Reserved11;
+
+    UCHAR A3Values[3];
+    UCHAR Reserved12;
+
+} CDROM_TOC_ATIP_DATA_BLOCK, *PCDROM_TOC_ATIP_DATA_BLOCK;
+
+typedef struct _CDROM_TOC_ATIP_DATA {
+    
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR Reserved1;
+    UCHAR Reserved2;
+
+    //
+    // zero? to N descriptors included.
+    //
+
+    CDROM_TOC_ATIP_DATA_BLOCK Descriptors[0];
+
+} CDROM_TOC_ATIP_DATA, *PCDROM_TOC_ATIP_DATA;
+
+//
+// CD ROM Table OF Contents
+// Format 5 - CD Text Info
+//
+typedef struct _CDROM_TOC_CD_TEXT_DATA_BLOCK {
+    UCHAR PackType;
+    UCHAR TrackNumber       : 7;
+    UCHAR ExtensionFlag     : 1;  // should be zero!
+    UCHAR SequenceNumber;
+    UCHAR CharacterPosition : 4;
+    UCHAR BlockNumber       : 3;
+    UCHAR Unicode           : 1;
+    union {
+        UCHAR Text[12];
+        WCHAR WText[6];
+    };
+    UCHAR CRC[2];
+} CDROM_TOC_CD_TEXT_DATA_BLOCK, *PCDROM_TOC_CD_TEXT_DATA_BLOCK;
+
+typedef struct _CDROM_TOC_CD_TEXT_DATA {
+    
+    //
+    // Header
+    //
+
+    UCHAR Length[2];  // add two bytes for this field
+    UCHAR Reserved1;
+    UCHAR Reserved2;
+    
+    //
+    // the text info comes in discrete blocks of
+    // a heavily-overloaded structure
+    //
+    
+    CDROM_TOC_CD_TEXT_DATA_BLOCK Descriptors[0];
+
+} CDROM_TOC_CD_TEXT_DATA, *PCDROM_TOC_CD_TEXT_DATA;
+
+//
+// These are the types used for PackType field in CDROM_TOC_CD_TEXT_DATA_BLOCK
+// and also for requesting specific info from IOCTL_CDROM_READ_CD_TEXT
+//
+#define CDROM_CD_TEXT_PACK_ALBUM_NAME 0x80
+#define CDROM_CD_TEXT_PACK_PERFORMER  0x81
+#define CDROM_CD_TEXT_PACK_SONGWRITER 0x82
+#define CDROM_CD_TEXT_PACK_COMPOSER   0x83
+#define CDROM_CD_TEXT_PACK_ARRANGER   0x84
+#define CDROM_CD_TEXT_PACK_MESSAGES   0x85
+#define CDROM_CD_TEXT_PACK_DISC_ID    0x86
+#define CDROM_CD_TEXT_PACK_GENRE      0x87
+#define CDROM_CD_TEXT_PACK_TOC_INFO   0x88
+#define CDROM_CD_TEXT_PACK_TOC_INFO2  0x89
+// 0x8a - 0x8d are reserved....
+#define CDROM_CD_TEXT_PACK_UPC_EAN    0x8e
+#define CDROM_CD_TEXT_PACK_SIZE_INFO  0x8f
+
+//
+// Play audio starting at MSF and ending at MSF
+//
+
+typedef struct _CDROM_PLAY_AUDIO_MSF {
+    UCHAR StartingM;
+    UCHAR StartingS;
+    UCHAR StartingF;
+    UCHAR EndingM;
+    UCHAR EndingS;
+    UCHAR EndingF;
+} CDROM_PLAY_AUDIO_MSF, *PCDROM_PLAY_AUDIO_MSF;
+
+//
+// Seek to MSF
+//
+
+typedef struct _CDROM_SEEK_AUDIO_MSF {
+    UCHAR M;
+    UCHAR S;
+    UCHAR F;
+} CDROM_SEEK_AUDIO_MSF, *PCDROM_SEEK_AUDIO_MSF;
+
+
+//
+//  Flags for the disk type
+//
+
+typedef struct _CDROM_DISK_DATA {
+
+    ULONG DiskData;
+
+} CDROM_DISK_DATA, *PCDROM_DISK_DATA;
+
+#define CDROM_DISK_AUDIO_TRACK      (0x00000001)
+#define CDROM_DISK_DATA_TRACK       (0x00000002)
+
+//
+// CD ROM Data Mode Codes, used with IOCTL_CDROM_READ_Q_CHANNEL
+//
+
+#define IOCTL_CDROM_SUB_Q_CHANNEL    0x00
+#define IOCTL_CDROM_CURRENT_POSITION 0x01
+#define IOCTL_CDROM_MEDIA_CATALOG    0x02
+#define IOCTL_CDROM_TRACK_ISRC       0x03
+
+typedef struct _CDROM_SUB_Q_DATA_FORMAT {
+    UCHAR Format;
+    UCHAR Track;
+} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT;
+
+
+//
+// CD ROM Sub-Q Channel Data Format
+//
+
+typedef struct _SUB_Q_HEADER {
+    UCHAR Reserved;
+    UCHAR AudioStatus;
+    UCHAR DataLength[2];
+} SUB_Q_HEADER, *PSUB_Q_HEADER;
+
+typedef struct _SUB_Q_CURRENT_POSITION {
+    SUB_Q_HEADER Header;
+    UCHAR FormatCode;
+    UCHAR Control : 4;
+    UCHAR ADR : 4;
+    UCHAR TrackNumber;
+    UCHAR IndexNumber;
+    UCHAR AbsoluteAddress[4];
+    UCHAR TrackRelativeAddress[4];
+} SUB_Q_CURRENT_POSITION, *PSUB_Q_CURRENT_POSITION;
+
+typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER {
+    SUB_Q_HEADER Header;
+    UCHAR FormatCode;
+    UCHAR Reserved[3];
+    UCHAR Reserved1 : 7;
+    UCHAR Mcval : 1;
+    UCHAR MediaCatalog[15];
+} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER;
+
+typedef struct _SUB_Q_TRACK_ISRC {
+    SUB_Q_HEADER Header;
+    UCHAR FormatCode;
+    UCHAR Reserved0;
+    UCHAR Track;
+    UCHAR Reserved1;
+    UCHAR Reserved2 : 7;
+    UCHAR Tcval : 1;
+    UCHAR TrackIsrc[15];
+} SUB_Q_TRACK_ISRC, *PSUB_Q_TRACK_ISRC;
+
+typedef union _SUB_Q_CHANNEL_DATA {
+    SUB_Q_CURRENT_POSITION CurrentPosition;
+    SUB_Q_MEDIA_CATALOG_NUMBER MediaCatalog;
+    SUB_Q_TRACK_ISRC TrackIsrc;
+} SUB_Q_CHANNEL_DATA, *PSUB_Q_CHANNEL_DATA;
+
+//
+// Audio Status Codes
+//
+
+#define AUDIO_STATUS_NOT_SUPPORTED  0x00
+#define AUDIO_STATUS_IN_PROGRESS    0x11
+#define AUDIO_STATUS_PAUSED         0x12
+#define AUDIO_STATUS_PLAY_COMPLETE  0x13
+#define AUDIO_STATUS_PLAY_ERROR     0x14
+#define AUDIO_STATUS_NO_STATUS      0x15
+
+//
+// ADR Sub-channel Q Field
+//
+
+#define ADR_NO_MODE_INFORMATION     0x0
+#define ADR_ENCODES_CURRENT_POSITION 0x1
+#define ADR_ENCODES_MEDIA_CATALOG   0x2
+#define ADR_ENCODES_ISRC            0x3
+
+//
+// Sub-channel Q Control Bits
+//
+
+#define AUDIO_WITH_PREEMPHASIS      0x1
+#define DIGITAL_COPY_PERMITTED      0x2
+#define AUDIO_DATA_TRACK            0x4
+#define TWO_FOUR_CHANNEL_AUDIO      0x8
+
+//
+// Get Audio control parameters
+//
+
+typedef struct _CDROM_AUDIO_CONTROL {
+    UCHAR LbaFormat;
+    USHORT LogicalBlocksPerSecond;
+} CDROM_AUDIO_CONTROL, *PCDROM_AUDIO_CONTROL;
+
+//
+// Volume control - Volume takes a value between 1 and 0xFF.
+// SCSI-II CDROM audio suppports up to 4 audio ports with
+// Independent volume control.
+//
+
+typedef struct _VOLUME_CONTROL {
+    UCHAR PortVolume[4];
+} VOLUME_CONTROL, *PVOLUME_CONTROL;
+
+typedef enum _TRACK_MODE_TYPE {
+    YellowMode2,
+    XAForm2,
+    CDDA
+} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;
+
+//
+// Passed to cdrom to describe the raw read, ie. Mode 2, Form 2, CDDA...
+//
+
+typedef struct __RAW_READ_INFO {
+    LARGE_INTEGER DiskOffset;
+    ULONG    SectorCount;
+    TRACK_MODE_TYPE TrackMode;
+} RAW_READ_INFO, *PRAW_READ_INFO;
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#if _MSC_VER >= 1200
+#pragma warning(pop)          // un-sets any local warning changes
+#else
+#pragma warning(default:4200) // array[0] is not a warning for this file
+#pragma warning(default:4201) // nameless struct/unions
+#pragma warning(default:4214) // bit fields other than int
+#endif
+
+
+#endif  // _NTDDCDRM_
+
+// end_winioctl
+
+
diff --git a/vsfilter/include/winddk/ntddcdvd.h b/vsfilter/include/winddk/ntddcdvd.h
new file mode 100644
index 0000000000000000000000000000000000000000..3498c7a4a7d38e5489ee296b90cd1917315352c5
--- /dev/null
+++ b/vsfilter/include/winddk/ntddcdvd.h
@@ -0,0 +1,294 @@
+/*++ BUILD Version: 0001    // Increment this if a change has global effects
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    ntddcdvd.h
+
+Abstract:
+
+    This module contains structures and definitions
+    associated with DVD ioctls.
+
+    This module is used in conjunction with ntddcdrm.h which contains the
+    cdrom specific ioctls which will work on CDVD drives
+
+Author:
+
+    Peter Wieland
+
+Revision History:
+
+--*/
+
+// begin_winioctl
+
+#ifndef _NTDDCDVD_
+#define _NTDDCDVD_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <pshpack1.h>
+
+
+//
+// NtDeviceIoControlFile IoControlCode values for this device.
+//
+// Warning:  Remember that the low two bits of the code specify how the
+//           buffers are passed to the driver!
+//
+
+#define IOCTL_DVD_BASE                 FILE_DEVICE_DVD
+
+//
+// CDVD Device Control Functions
+//
+// Warning: Ioctls from 200 through 300 are used for the old common class
+// driver ioctls and should not be used for device specific functionality
+//
+
+//
+// Copyright ioctls
+//
+
+#define IOCTL_DVD_START_SESSION     CTL_CODE(IOCTL_DVD_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_READ_KEY          CTL_CODE(IOCTL_DVD_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_SEND_KEY          CTL_CODE(IOCTL_DVD_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_END_SESSION       CTL_CODE(IOCTL_DVD_BASE, 0x0403, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_SET_READ_AHEAD    CTL_CODE(IOCTL_DVD_BASE, 0x0404, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_GET_REGION        CTL_CODE(IOCTL_DVD_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_DVD_SEND_KEY2         CTL_CODE(IOCTL_DVD_BASE, 0x0406, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+//
+// DVD Structure queries
+//
+
+#define IOCTL_DVD_READ_STRUCTURE    CTL_CODE(IOCTL_DVD_BASE, 0x0450, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+//
+// The following file contains the IOCTL_STORAGE class ioctl definitions
+//
+
+#define IOCTL_STORAGE_SET_READ_AHEAD        CTL_CODE(IOCTL_STORAGE_BASE, 0x0100, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+// end_winioctl
+
+//#include <ntddstor.h>
+#include "ntddstor.h"
+
+// begin_winioctl
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    DvdChallengeKey = 0x01,
+    DvdBusKey1,
+    DvdBusKey2,
+    DvdTitleKey,
+    DvdAsf,
+    DvdSetRpcKey = 0x6,
+    DvdGetRpcKey = 0x8,
+    DvdDiskKey = 0x80,
+    DvdInvalidateAGID = 0x3f
+} DVD_KEY_TYPE;
+
+typedef ULONG DVD_SESSION_ID, *PDVD_SESSION_ID;
+
+typedef struct _DVD_COPY_PROTECT_KEY {
+    ULONG KeyLength;
+    DVD_SESSION_ID SessionId;
+    DVD_KEY_TYPE KeyType;
+    ULONG KeyFlags;
+    union {
+        HANDLE FileHandle;
+        LARGE_INTEGER TitleOffset;
+    } Parameters;
+    UCHAR KeyData[0];
+} DVD_COPY_PROTECT_KEY, *PDVD_COPY_PROTECT_KEY;
+
+//
+// Predefined (Mt. Fuji) key sizes
+// Add sizeof(DVD_COPY_PROTECT_KEY) to get allocation size for
+// the full key structure
+//
+
+#define DVD_CHALLENGE_KEY_LENGTH    (12 + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_BUS_KEY_LENGTH          (8 + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_TITLE_KEY_LENGTH        (8 + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_DISK_KEY_LENGTH         (2048 + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_RPC_KEY_LENGTH          (sizeof(DVD_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_SET_RPC_KEY_LENGTH      (sizeof(DVD_SET_RPC_KEY) + sizeof(DVD_COPY_PROTECT_KEY))
+#define DVD_ASF_LENGTH              (sizeof(DVD_ASF) + sizeof(DVD_COPY_PROTECT_KEY))
+
+//
+// Used with IOCTL_DVD_END_SESSION to end all DVD sessions at once
+//
+
+#define DVD_END_ALL_SESSIONS ((DVD_SESSION_ID) 0xffffffff)
+
+//
+// CGMS Copy Protection Flags
+//
+
+#define DVD_CGMS_RESERVED_MASK      0x00000078
+
+#define DVD_CGMS_COPY_PROTECT_MASK  0x00000018
+#define DVD_CGMS_COPY_PERMITTED     0x00000000
+#define DVD_CGMS_COPY_ONCE          0x00000010
+#define DVD_CGMS_NO_COPY            0x00000018
+
+#define DVD_COPYRIGHT_MASK          0x00000040
+#define DVD_NOT_COPYRIGHTED         0x00000000
+#define DVD_COPYRIGHTED             0x00000040
+
+#define DVD_SECTOR_PROTECT_MASK     0x00000020
+#define DVD_SECTOR_NOT_PROTECTED    0x00000000
+#define DVD_SECTOR_PROTECTED        0x00000020
+
+/*++
+
+IOCTL_STORAGE_SET_READ_AHEAD
+
+Requests that the storage device skip to TargetAddress once it has run across
+TriggerAddress during the course of it's read-ahead caching operations.
+
+Input:
+
+    a STORAGE_SET_READ_AHEAD structure which contains:
+        * the trigger address
+        * the target address
+
+Output:
+
+    none
+
+--*/
+
+typedef struct _STORAGE_SET_READ_AHEAD {
+    LARGE_INTEGER TriggerAddress;
+    LARGE_INTEGER TargetAddress;
+} STORAGE_SET_READ_AHEAD, *PSTORAGE_SET_READ_AHEAD;
+
+/*++
+
+IOCTL_DVD_READ_STRUCTURE
+
+Issues a READ_DVD_STRUCTURE command to the drive.
+
+Input:
+
+    a DVD_READ_STRUCTURE describing what information is requested
+
+Output:
+
+    a DVD Layer Descriptor as defined below
+
+--*/
+
+typedef enum DVD_STRUCTURE_FORMAT {
+    DvdPhysicalDescriptor,
+    DvdCopyrightDescriptor,
+    DvdDiskKeyDescriptor,
+    DvdBCADescriptor,
+    DvdManufacturerDescriptor,
+    DvdMaxDescriptor
+} DVD_STRUCTURE_FORMAT, *PDVD_STRUCTURE_FORMAT;
+
+typedef struct DVD_READ_STRUCTURE {
+    LARGE_INTEGER BlockByteOffset;
+    DVD_STRUCTURE_FORMAT Format;
+    DVD_SESSION_ID SessionId;
+    UCHAR LayerNumber;
+} DVD_READ_STRUCTURE, *PDVD_READ_STRUCTURE;
+
+typedef struct _DVD_DESCRIPTOR_HEADER {
+    USHORT Length;
+    UCHAR Reserved[2];
+    UCHAR Data[0];
+} DVD_DESCRIPTOR_HEADER, *PDVD_DESCRIPTOR_HEADER;
+
+typedef struct _DVD_LAYER_DESCRIPTOR {
+    UCHAR BookVersion : 4;
+    UCHAR BookType : 4;
+    UCHAR MinimumRate : 4;
+    UCHAR DiskSize : 4;
+    UCHAR LayerType : 4;
+    UCHAR TrackPath : 1;
+    UCHAR NumberOfLayers : 2;
+    UCHAR Reserved1 : 1;
+    UCHAR TrackDensity : 4;
+    UCHAR LinearDensity : 4;
+    ULONG StartingDataSector;
+    ULONG EndDataSector;
+    ULONG EndLayerZeroSector;
+    UCHAR Reserved5 : 7;
+    UCHAR BCAFlag : 1;
+    UCHAR Reserved6;
+} DVD_LAYER_DESCRIPTOR, *PDVD_LAYER_DESCRIPTOR;
+
+typedef struct _DVD_COPYRIGHT_DESCRIPTOR {
+    UCHAR CopyrightProtectionType;
+    UCHAR RegionManagementInformation;
+    USHORT Reserved;
+} DVD_COPYRIGHT_DESCRIPTOR, *PDVD_COPYRIGHT_DESCRIPTOR;
+
+typedef struct _DVD_DISK_KEY_DESCRIPTOR {
+    UCHAR DiskKeyData[2048];
+} DVD_DISK_KEY_DESCRIPTOR, *PDVD_DISK_KEY_DESCRIPTOR;
+
+typedef struct _DVD_BCA_DESCRIPTOR {
+    UCHAR BCAInformation[0];
+} DVD_BCA_DESCRIPTOR, *PDVD_BCA_DESCRIPTOR;
+
+typedef struct _DVD_MANUFACTURER_DESCRIPTOR {
+    UCHAR ManufacturingInformation[2048];
+} DVD_MANUFACTURER_DESCRIPTOR, *PDVD_MANUFACTURER_DESCRIPTOR;
+
+typedef struct _DVD_RPC_KEY {
+    UCHAR UserResetsAvailable:3;
+    UCHAR ManufacturerResetsAvailable:3;
+    UCHAR TypeCode:2;
+    UCHAR RegionMask;
+    UCHAR RpcScheme;
+    UCHAR Reserved2[1];
+} DVD_RPC_KEY, * PDVD_RPC_KEY;
+
+typedef struct _DVD_SET_RPC_KEY {
+    UCHAR PreferredDriveRegionCode;
+    UCHAR Reserved[3];
+} DVD_SET_RPC_KEY, * PDVD_SET_RPC_KEY;
+
+typedef struct _DVD_ASF {
+    UCHAR Reserved0[3];
+    UCHAR SuccessFlag:1;
+    UCHAR Reserved1:7;
+} DVD_ASF, * PDVD_ASF;
+
+#if 0
+typedef struct _DVD_REGION {
+     UCHAR CopySystem;
+     UCHAR RegionData;                      // current media region (not playable when set)
+     UCHAR SystemRegion;                    // current drive region (playable when set)
+     UCHAR ResetCount;                      // number of resets available
+} DVD_REGION, *PDVD_REGION;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#include <poppack.h>
+
+
+#endif  // _NTDDCDVD_
+
+// end_winioctl
+
+
diff --git a/vsfilter/include/winddk/ntddstor.h b/vsfilter/include/winddk/ntddstor.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d9ba82b9113f1fd02459e43de3065843faea274
--- /dev/null
+++ b/vsfilter/include/winddk/ntddstor.h
@@ -0,0 +1,748 @@
+/*++ BUILD Version: 0001    // Increment this if a change has global effects
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+    ntddstor.h
+
+Abstract:
+
+    This is the include file that defines all common constants and types
+    accessing the storage class drivers
+
+Author:
+
+    Peter Wieland 19-Jun-1996
+
+Revision History:
+
+--*/
+
+#pragma once
+
+#include "devioctl.h"
+
+//
+// Interface GUIDs
+//
+// need these GUIDs outside conditional includes so that user can
+//   #include <ntddstor.h> in precompiled header
+//   #include <initguid.h> in a single source file
+//   #include <ntddstor.h> in that source file a second time to instantiate the GUIDs
+//
+#ifdef DEFINE_GUID
+//
+// Make sure FAR is defined...
+//
+#ifndef FAR
+#ifdef _WIN32
+#define FAR
+#else
+#define FAR _far
+#endif
+#endif
+
+// begin_wioctlguids
+DEFINE_GUID(GUID_DEVINTERFACE_DISK,                   0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_CDROM,                  0x53f56308L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_PARTITION,              0x53f5630aL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_TAPE,                   0x53f5630bL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_WRITEONCEDISK,          0x53f5630cL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_VOLUME,                 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_MEDIUMCHANGER,          0x53f56310L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_FLOPPY,                 0x53f56311L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_CDCHANGER,              0x53f56312L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT,            0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+// end_wioctlguids
+
+// begin_wioctlobsoleteguids
+#define DiskClassGuid               GUID_DEVINTERFACE_DISK
+#define CdRomClassGuid              GUID_DEVINTERFACE_CDROM
+#define PartitionClassGuid          GUID_DEVINTERFACE_PARTITION
+#define TapeClassGuid               GUID_DEVINTERFACE_TAPE
+#define WriteOnceDiskClassGuid      GUID_DEVINTERFACE_WRITEONCEDISK
+#define VolumeClassGuid             GUID_DEVINTERFACE_VOLUME
+#define MediumChangerClassGuid      GUID_DEVINTERFACE_MEDIUMCHANGER
+#define FloppyClassGuid             GUID_DEVINTERFACE_FLOPPY
+#define CdChangerClassGuid          GUID_DEVINTERFACE_CDCHANGER
+#define StoragePortClassGuid        GUID_DEVINTERFACE_STORAGEPORT
+// end_wioctlobsoleteguids
+#endif
+
+// begin_winioctl
+
+#ifndef _NTDDSTOR_H_
+#define _NTDDSTOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// IoControlCode values for storage devices
+//
+
+#define IOCTL_STORAGE_BASE FILE_DEVICE_MASS_STORAGE
+
+//
+// The following device control codes are common for all class drivers.  They
+// should be used in place of the older IOCTL_DISK, IOCTL_CDROM and IOCTL_TAPE
+// common codes
+//
+
+#define IOCTL_STORAGE_CHECK_VERIFY            CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_CHECK_VERIFY2           CTL_CODE(IOCTL_STORAGE_BASE, 0x0200, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_MEDIA_REMOVAL           CTL_CODE(IOCTL_STORAGE_BASE, 0x0201, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_EJECT_MEDIA             CTL_CODE(IOCTL_STORAGE_BASE, 0x0202, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_LOAD_MEDIA              CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_LOAD_MEDIA2             CTL_CODE(IOCTL_STORAGE_BASE, 0x0203, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_RESERVE                 CTL_CODE(IOCTL_STORAGE_BASE, 0x0204, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_RELEASE                 CTL_CODE(IOCTL_STORAGE_BASE, 0x0205, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_FIND_NEW_DEVICES        CTL_CODE(IOCTL_STORAGE_BASE, 0x0206, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_STORAGE_EJECTION_CONTROL        CTL_CODE(IOCTL_STORAGE_BASE, 0x0250, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_MCN_CONTROL             CTL_CODE(IOCTL_STORAGE_BASE, 0x0251, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_STORAGE_GET_MEDIA_TYPES         CTL_CODE(IOCTL_STORAGE_BASE, 0x0300, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_GET_MEDIA_TYPES_EX      CTL_CODE(IOCTL_STORAGE_BASE, 0x0301, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER CTL_CODE(IOCTL_STORAGE_BASE, 0x0304, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_GET_HOTPLUG_INFO        CTL_CODE(IOCTL_STORAGE_BASE, 0x0305, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_SET_HOTPLUG_INFO        CTL_CODE(IOCTL_STORAGE_BASE, 0x0306, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_STORAGE_RESET_BUS               CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_RESET_DEVICE            CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_STORAGE_BREAK_RESERVATION       CTL_CODE(IOCTL_STORAGE_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define IOCTL_STORAGE_GET_DEVICE_NUMBER       CTL_CODE(IOCTL_STORAGE_BASE, 0x0420, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_STORAGE_PREDICT_FAILURE         CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// end_winioctl
+
+
+#define IOCTL_STORAGE_QUERY_PROPERTY   CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+// begin_winioctl
+
+//
+// These ioctl codes are obsolete.  They are defined here to avoid resuing them
+// and to allow class drivers to respond to them more easily.
+//
+
+#define OBSOLETE_IOCTL_STORAGE_RESET_BUS        CTL_CODE(IOCTL_STORAGE_BASE, 0x0400, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define OBSOLETE_IOCTL_STORAGE_RESET_DEVICE     CTL_CODE(IOCTL_STORAGE_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+
+//
+// IOCTL_STORAGE_GET_HOTPLUG_INFO
+//
+
+typedef struct _STORAGE_HOTPLUG_INFO {
+    ULONG Size; // version
+    BOOLEAN MediaRemovable; // ie. zip, jaz, cdrom, mo, etc. vs hdd
+    BOOLEAN MediaHotplug;   // ie. does the device succeed a lock even though its not lockable media?
+    BOOLEAN DeviceHotplug;  // ie. 1394, USB, etc.
+    BOOLEAN WriteCacheEnableOverride; // This field should not be relied upon because it is no longer used
+} STORAGE_HOTPLUG_INFO, *PSTORAGE_HOTPLUG_INFO;
+
+//
+// IOCTL_STORAGE_GET_DEVICE_NUMBER
+//
+// input - none
+//
+// output - STORAGE_DEVICE_NUMBER structure
+//          The values in the STORAGE_DEVICE_NUMBER structure are guaranteed
+//          to remain unchanged until the system is rebooted.  They are not
+//          guaranteed to be persistant across boots.
+//
+
+typedef struct _STORAGE_DEVICE_NUMBER {
+
+    //
+    // The FILE_DEVICE_XXX type for this device.
+    //
+
+    DEVICE_TYPE DeviceType;
+
+    //
+    // The number of this device
+    //
+
+    ULONG       DeviceNumber;
+
+    //
+    // If the device is partitionable, the partition number of the device.
+    // Otherwise -1
+    //
+
+    ULONG       PartitionNumber;
+} STORAGE_DEVICE_NUMBER, *PSTORAGE_DEVICE_NUMBER;
+
+//
+// Define the structures for scsi resets
+//
+
+typedef struct _STORAGE_BUS_RESET_REQUEST {
+    UCHAR PathId;
+} STORAGE_BUS_RESET_REQUEST, *PSTORAGE_BUS_RESET_REQUEST;
+
+//
+// IOCTL_STORAGE_MEDIA_REMOVAL disables the mechanism
+// on a storage device that ejects media. This function
+// may or may not be supported on storage devices that
+// support removable media.
+//
+// TRUE means prevent media from being removed.
+// FALSE means allow media removal.
+//
+
+typedef struct _PREVENT_MEDIA_REMOVAL {
+    BOOLEAN PreventMediaRemoval;
+} PREVENT_MEDIA_REMOVAL, *PPREVENT_MEDIA_REMOVAL;
+
+// begin_ntminitape
+
+
+typedef struct _TAPE_STATISTICS {
+    ULONG Version;
+    ULONG Flags;
+    LARGE_INTEGER RecoveredWrites;
+    LARGE_INTEGER UnrecoveredWrites;
+    LARGE_INTEGER RecoveredReads;
+    LARGE_INTEGER UnrecoveredReads;
+    UCHAR         CompressionRatioReads;
+    UCHAR         CompressionRatioWrites;
+} TAPE_STATISTICS, *PTAPE_STATISTICS;
+
+#define RECOVERED_WRITES_VALID   0x00000001
+#define UNRECOVERED_WRITES_VALID 0x00000002
+#define RECOVERED_READS_VALID    0x00000004
+#define UNRECOVERED_READS_VALID  0x00000008
+#define WRITE_COMPRESSION_INFO_VALID  0x00000010
+#define READ_COMPRESSION_INFO_VALID   0x00000020
+
+typedef struct _TAPE_GET_STATISTICS {
+    ULONG Operation;
+} TAPE_GET_STATISTICS, *PTAPE_GET_STATISTICS;
+
+#define TAPE_RETURN_STATISTICS 0L
+#define TAPE_RETURN_ENV_INFO   1L
+#define TAPE_RESET_STATISTICS  2L
+
+//
+// IOCTL_STORAGE_GET_MEDIA_TYPES_EX will return an array of DEVICE_MEDIA_INFO
+// structures, one per supported type, embedded in the GET_MEDIA_TYPES struct.
+//
+
+typedef enum _STORAGE_MEDIA_TYPE {
+    //
+    // Following are defined in ntdddisk.h in the MEDIA_TYPE enum
+    //
+    // Unknown,                // Format is unknown
+    // F5_1Pt2_512,            // 5.25", 1.2MB,  512 bytes/sector
+    // F3_1Pt44_512,           // 3.5",  1.44MB, 512 bytes/sector
+    // F3_2Pt88_512,           // 3.5",  2.88MB, 512 bytes/sector
+    // F3_20Pt8_512,           // 3.5",  20.8MB, 512 bytes/sector
+    // F3_720_512,             // 3.5",  720KB,  512 bytes/sector
+    // F5_360_512,             // 5.25", 360KB,  512 bytes/sector
+    // F5_320_512,             // 5.25", 320KB,  512 bytes/sector
+    // F5_320_1024,            // 5.25", 320KB,  1024 bytes/sector
+    // F5_180_512,             // 5.25", 180KB,  512 bytes/sector
+    // F5_160_512,             // 5.25", 160KB,  512 bytes/sector
+    // RemovableMedia,         // Removable media other than floppy
+    // FixedMedia,             // Fixed hard disk media
+    // F3_120M_512,            // 3.5", 120M Floppy
+    // F3_640_512,             // 3.5" ,  640KB,  512 bytes/sector
+    // F5_640_512,             // 5.25",  640KB,  512 bytes/sector
+    // F5_720_512,             // 5.25",  720KB,  512 bytes/sector
+    // F3_1Pt2_512,            // 3.5" ,  1.2Mb,  512 bytes/sector
+    // F3_1Pt23_1024,          // 3.5" ,  1.23Mb, 1024 bytes/sector
+    // F5_1Pt23_1024,          // 5.25",  1.23MB, 1024 bytes/sector
+    // F3_128Mb_512,           // 3.5" MO 128Mb   512 bytes/sector
+    // F3_230Mb_512,           // 3.5" MO 230Mb   512 bytes/sector
+    // F8_256_128,             // 8",     256KB,  128 bytes/sector
+    // F3_200Mb_512,           // 3.5",   200M Floppy (HiFD)
+    //
+
+    DDS_4mm = 0x20,            // Tape - DAT DDS1,2,... (all vendors)
+    MiniQic,                   // Tape - miniQIC Tape
+    Travan,                    // Tape - Travan TR-1,2,3,...
+    QIC,                       // Tape - QIC
+    MP_8mm,                    // Tape - 8mm Exabyte Metal Particle
+    AME_8mm,                   // Tape - 8mm Exabyte Advanced Metal Evap
+    AIT1_8mm,                  // Tape - 8mm Sony AIT
+    DLT,                       // Tape - DLT Compact IIIxt, IV
+    NCTP,                      // Tape - Philips NCTP
+    IBM_3480,                  // Tape - IBM 3480
+    IBM_3490E,                 // Tape - IBM 3490E
+    IBM_Magstar_3590,          // Tape - IBM Magstar 3590
+    IBM_Magstar_MP,            // Tape - IBM Magstar MP
+    STK_DATA_D3,               // Tape - STK Data D3
+    SONY_DTF,                  // Tape - Sony DTF
+    DV_6mm,                    // Tape - 6mm Digital Video
+    DMI,                       // Tape - Exabyte DMI and compatibles
+    SONY_D2,                   // Tape - Sony D2S and D2L
+    CLEANER_CARTRIDGE,         // Cleaner - All Drive types that support Drive Cleaners
+    CD_ROM,                    // Opt_Disk - CD
+    CD_R,                      // Opt_Disk - CD-Recordable (Write Once)
+    CD_RW,                     // Opt_Disk - CD-Rewriteable
+    DVD_ROM,                   // Opt_Disk - DVD-ROM
+    DVD_R,                     // Opt_Disk - DVD-Recordable (Write Once)
+    DVD_RW,                    // Opt_Disk - DVD-Rewriteable
+    MO_3_RW,                   // Opt_Disk - 3.5" Rewriteable MO Disk
+    MO_5_WO,                   // Opt_Disk - MO 5.25" Write Once
+    MO_5_RW,                   // Opt_Disk - MO 5.25" Rewriteable (not LIMDOW)
+    MO_5_LIMDOW,               // Opt_Disk - MO 5.25" Rewriteable (LIMDOW)
+    PC_5_WO,                   // Opt_Disk - Phase Change 5.25" Write Once Optical
+    PC_5_RW,                   // Opt_Disk - Phase Change 5.25" Rewriteable
+    PD_5_RW,                   // Opt_Disk - PhaseChange Dual Rewriteable
+    ABL_5_WO,                  // Opt_Disk - Ablative 5.25" Write Once Optical
+    PINNACLE_APEX_5_RW,        // Opt_Disk - Pinnacle Apex 4.6GB Rewriteable Optical
+    SONY_12_WO,                // Opt_Disk - Sony 12" Write Once
+    PHILIPS_12_WO,             // Opt_Disk - Philips/LMS 12" Write Once
+    HITACHI_12_WO,             // Opt_Disk - Hitachi 12" Write Once
+    CYGNET_12_WO,              // Opt_Disk - Cygnet/ATG 12" Write Once
+    KODAK_14_WO,               // Opt_Disk - Kodak 14" Write Once
+    MO_NFR_525,                // Opt_Disk - Near Field Recording (Terastor)
+    NIKON_12_RW,               // Opt_Disk - Nikon 12" Rewriteable
+    IOMEGA_ZIP,                // Mag_Disk - Iomega Zip
+    IOMEGA_JAZ,                // Mag_Disk - Iomega Jaz
+    SYQUEST_EZ135,             // Mag_Disk - Syquest EZ135
+    SYQUEST_EZFLYER,           // Mag_Disk - Syquest EzFlyer
+    SYQUEST_SYJET,             // Mag_Disk - Syquest SyJet
+    AVATAR_F2,                 // Mag_Disk - 2.5" Floppy
+    MP2_8mm,                   // Tape - 8mm Hitachi
+    DST_S,                     // Ampex DST Small Tapes
+    DST_M,                     // Ampex DST Medium Tapes
+    DST_L,                     // Ampex DST Large Tapes
+    VXATape_1,                 // Ecrix 8mm Tape
+    VXATape_2,                 // Ecrix 8mm Tape
+    STK_9840,                  // STK 9840
+    LTO_Ultrium,               // IBM, HP, Seagate LTO Ultrium
+    LTO_Accelis,               // IBM, HP, Seagate LTO Accelis
+    DVD_RAM,                   // Opt_Disk - DVD-RAM
+    AIT_8mm,                   // AIT2 or higher
+    ADR_1,                     // OnStream ADR Mediatypes
+    ADR_2
+} STORAGE_MEDIA_TYPE, *PSTORAGE_MEDIA_TYPE;
+
+#define MEDIA_ERASEABLE         0x00000001
+#define MEDIA_WRITE_ONCE        0x00000002
+#define MEDIA_READ_ONLY         0x00000004
+#define MEDIA_READ_WRITE        0x00000008
+
+#define MEDIA_WRITE_PROTECTED   0x00000100
+#define MEDIA_CURRENTLY_MOUNTED 0x80000000
+
+//
+// Define the different storage bus types
+// Bus types below 128 (0x80) are reserved for Microsoft use
+//
+
+typedef enum _STORAGE_BUS_TYPE {
+    BusTypeUnknown = 0x00,
+    BusTypeScsi,
+    BusTypeAtapi,
+    BusTypeAta,
+    BusType1394,
+    BusTypeSsa,
+    BusTypeFibre,
+    BusTypeUsb,
+    BusTypeRAID,
+    BusTypeMaxReserved = 0x7F
+} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
+
+typedef struct _DEVICE_MEDIA_INFO {
+    union {
+        struct {
+            LARGE_INTEGER Cylinders;
+            STORAGE_MEDIA_TYPE MediaType;
+            ULONG TracksPerCylinder;
+            ULONG SectorsPerTrack;
+            ULONG BytesPerSector;
+            ULONG NumberMediaSides;
+            ULONG MediaCharacteristics; // Bitmask of MEDIA_XXX values.
+        } DiskInfo;
+
+        struct {
+            LARGE_INTEGER Cylinders;
+            STORAGE_MEDIA_TYPE MediaType;
+            ULONG TracksPerCylinder;
+            ULONG SectorsPerTrack;
+            ULONG BytesPerSector;
+            ULONG NumberMediaSides;
+            ULONG MediaCharacteristics; // Bitmask of MEDIA_XXX values.
+        } RemovableDiskInfo;
+
+        struct {
+            STORAGE_MEDIA_TYPE MediaType;
+            ULONG   MediaCharacteristics; // Bitmask of MEDIA_XXX values.
+            ULONG   CurrentBlockSize;
+            STORAGE_BUS_TYPE BusType;
+
+            //
+            // Bus specific information describing the medium supported.
+            //
+
+            union {
+                struct {
+                    UCHAR MediumType;
+                    UCHAR DensityCode;
+                } ScsiInformation;
+            } BusSpecificData;
+
+        } TapeInfo;
+    } DeviceSpecific;
+} DEVICE_MEDIA_INFO, *PDEVICE_MEDIA_INFO;
+
+typedef struct _GET_MEDIA_TYPES {
+    ULONG DeviceType;              // FILE_DEVICE_XXX values
+    ULONG MediaInfoCount;
+    DEVICE_MEDIA_INFO MediaInfo[1];
+} GET_MEDIA_TYPES, *PGET_MEDIA_TYPES;
+
+
+//
+// IOCTL_STORAGE_PREDICT_FAILURE
+//
+// input - none
+//
+// output - STORAGE_PREDICT_FAILURE structure
+//          PredictFailure returns zero if no failure predicted and non zero
+//                         if a failure is predicted.
+//
+//          VendorSpecific returns 512 bytes of vendor specific information
+//                         if a failure is predicted
+//
+typedef struct _STORAGE_PREDICT_FAILURE
+{
+    ULONG PredictFailure;
+    UCHAR VendorSpecific[512];
+} STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE;
+
+// end_ntminitape
+// end_winioctl
+
+//
+// Property Query Structures
+//
+
+//
+// IOCTL_STORAGE_QUERY_PROPERTY
+//
+// Input Buffer:
+//      a STORAGE_PROPERTY_QUERY structure which describes what type of query
+//      is being done, what property is being queried for, and any additional
+//      parameters which a particular property query requires.
+//
+//  Output Buffer:
+//      Contains a buffer to place the results of the query into.  Since all
+//      property descriptors can be cast into a STORAGE_DESCRIPTOR_HEADER,
+//      the IOCTL can be called once with a small buffer then again using
+//      a buffer as large as the header reports is necessary.
+//
+
+
+//
+// Types of queries
+//
+
+typedef enum _STORAGE_QUERY_TYPE {
+    PropertyStandardQuery = 0,          // Retrieves the descriptor
+    PropertyExistsQuery,                // Used to test whether the descriptor is supported
+    PropertyMaskQuery,                  // Used to retrieve a mask of writeable fields in the descriptor
+    PropertyQueryMaxDefined     // use to validate the value
+} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
+
+//
+// define some initial property id's
+//
+
+typedef enum _STORAGE_PROPERTY_ID {
+    StorageDeviceProperty = 0,
+    StorageAdapterProperty,
+    StorageDeviceIdProperty
+} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
+
+//
+// Query structure - additional parameters for specific queries can follow
+// the header
+//
+
+typedef struct _STORAGE_PROPERTY_QUERY {
+
+    //
+    // ID of the property being retrieved
+    //
+
+    STORAGE_PROPERTY_ID PropertyId;
+
+    //
+    // Flags indicating the type of query being performed
+    //
+
+    STORAGE_QUERY_TYPE QueryType;
+
+    //
+    // Space for additional parameters if necessary
+    //
+
+    UCHAR AdditionalParameters[1];
+
+} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
+
+//
+// Standard property descriptor header.  All property pages should use this
+// as their first element or should contain these two elements
+//
+
+typedef struct _STORAGE_DESCRIPTOR_HEADER {
+
+    ULONG Version;
+
+    ULONG Size;
+
+} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER;
+
+//
+// Device property descriptor - this is really just a rehash of the inquiry
+// data retrieved from a scsi device
+//
+// This may only be retrieved from a target device.  Sending this to the bus
+// will result in an error
+//
+
+typedef struct _STORAGE_DEVICE_DESCRIPTOR {
+
+    //
+    // Sizeof(STORAGE_DEVICE_DESCRIPTOR)
+    //
+
+    ULONG Version;
+
+    //
+    // Total size of the descriptor, including the space for additional
+    // data and id strings
+    //
+
+    ULONG Size;
+
+    //
+    // The SCSI-2 device type
+    //
+
+    UCHAR DeviceType;
+
+    //
+    // The SCSI-2 device type modifier (if any) - this may be zero
+    //
+
+    UCHAR DeviceTypeModifier;
+
+    //
+    // Flag indicating whether the device's media (if any) is removable.  This
+    // field should be ignored for media-less devices
+    //
+
+    BOOLEAN RemovableMedia;
+
+    //
+    // Flag indicating whether the device can support mulitple outstanding
+    // commands.  The actual synchronization in this case is the responsibility
+    // of the port driver.
+    //
+
+    BOOLEAN CommandQueueing;
+
+    //
+    // Byte offset to the zero-terminated ascii string containing the device's
+    // vendor id string.  For devices with no such ID this will be zero
+    //
+
+    ULONG VendorIdOffset;
+
+    //
+    // Byte offset to the zero-terminated ascii string containing the device's
+    // product id string.  For devices with no such ID this will be zero
+    //
+
+    ULONG ProductIdOffset;
+
+    //
+    // Byte offset to the zero-terminated ascii string containing the device's
+    // product revision string.  For devices with no such string this will be
+    // zero
+    //
+
+    ULONG ProductRevisionOffset;
+
+    //
+    // Byte offset to the zero-terminated ascii string containing the device's
+    // serial number.  For devices with no serial number this will be zero
+    //
+
+    ULONG SerialNumberOffset;
+
+    //
+    // Contains the bus type (as defined above) of the device.  It should be
+    // used to interpret the raw device properties at the end of this structure
+    // (if any)
+    //
+
+    STORAGE_BUS_TYPE BusType;
+
+    //
+    // The number of bytes of bus-specific data which have been appended to
+    // this descriptor
+    //
+
+    ULONG RawPropertiesLength;
+
+    //
+    // Place holder for the first byte of the bus specific property data
+    //
+
+    UCHAR RawDeviceProperties[1];
+
+} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
+
+
+//
+// Adapter properties
+//
+// This descriptor can be retrieved from a target device object of from the
+// device object for the bus.  Retrieving from the target device object will
+// forward the request to the underlying bus
+//
+
+typedef struct _STORAGE_ADAPTER_DESCRIPTOR {
+
+    ULONG Version;
+
+    ULONG Size;
+
+    ULONG MaximumTransferLength;
+
+    ULONG MaximumPhysicalPages;
+
+    ULONG AlignmentMask;
+
+    BOOLEAN AdapterUsesPio;
+
+    BOOLEAN AdapterScansDown;
+
+    BOOLEAN CommandQueueing;
+
+    BOOLEAN AcceleratedTransfer;
+
+    UCHAR BusType;
+
+    USHORT BusMajorVersion;
+
+    USHORT BusMinorVersion;
+
+} STORAGE_ADAPTER_DESCRIPTOR, *PSTORAGE_ADAPTER_DESCRIPTOR;
+
+//
+// Storage identification descriptor.
+// The definitions here are based on the SCSI/SBP vital product data
+// device identifier page.
+//
+
+typedef enum _STORAGE_IDENTIFIER_CODE_SET {
+    StorageIdCodeSetReserved = 0,
+    StorageIdCodeSetBinary = 1,
+    StorageIdCodeSetAscii = 2
+} STORAGE_IDENTIFIER_CODE_SET, *PSTORAGE_IDENTIFIER_CODE_SET;
+
+typedef enum _STORAGE_IDENTIFIER_TYPE {
+    StorageIdTypeVendorSpecific = 0,
+    StorageIdTypeVendorId = 1,
+    StorageIdTypeEUI64 = 2,
+    StorageIdTypeFCPHName = 3,
+    StorageIdTypePortRelative = 4
+} STORAGE_IDENTIFIER_TYPE, *PSTORAGE_IDENTIFIER_TYPE;
+
+typedef enum _STORAGE_ASSOCIATION_TYPE {
+    StorageIdAssocDevice = 0,
+    StorageIdAssocPort = 1
+} STORAGE_ASSOCIATION_TYPE, *PSTORAGE_ASSOCIATION_TYPE;
+
+typedef struct _STORAGE_IDENTIFIER {
+    STORAGE_IDENTIFIER_CODE_SET CodeSet;
+    STORAGE_IDENTIFIER_TYPE Type;
+    USHORT IdentifierSize;
+    USHORT NextOffset;
+
+    //
+    // Add new fields here since existing code depends on
+    // the above layout not changing.
+    //
+
+    STORAGE_ASSOCIATION_TYPE Association;
+
+    //
+    // The identifier is a variable length array of bytes.
+    //
+
+    UCHAR Identifier[1];
+} STORAGE_IDENTIFIER, *PSTORAGE_IDENTIFIER;
+
+typedef struct _STORAGE_DEVICE_ID_DESCRIPTOR {
+
+    ULONG Version;
+
+    ULONG Size;
+
+    //
+    // The number of identifiers reported by the device.
+    //
+
+    ULONG NumberOfIdentifiers;
+
+    //
+    // The following field is actually a variable length array of identification
+    // descriptors.  Unfortunately there's no C notation for an array of
+    // variable length structures so we're forced to just pretend.
+    //
+
+    UCHAR Identifiers[1];
+} STORAGE_DEVICE_ID_DESCRIPTOR, *PSTORAGE_DEVICE_ID_DESCRIPTOR;
+
+
+#pragma warning(push)
+#pragma warning(disable:4200)
+typedef struct _STORAGE_MEDIA_SERIAL_NUMBER_DATA {
+
+    USHORT Reserved;
+
+    //
+    // the SerialNumberLength will be set to zero
+    // if the command is supported and the media
+    // does not have a valid serial number.
+    //
+
+    USHORT SerialNumberLength;
+
+    //
+    // the following data is binary, and is not guaranteed
+    // to be NULL terminated.  this is an excercise for the
+    // caller.
+    //
+
+    UCHAR SerialNumber[0];
+
+} STORAGE_MEDIA_SERIAL_NUMBER_DATA, *PSTORAGE_MEDIA_SERIAL_NUMBER_DATA;
+#pragma warning(push)
+
+
+// begin_winioctl
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _NTDDSTOR_H_
+// end_winioctl
+
diff --git a/vsfilter/libssf/Arabic.cpp b/vsfilter/libssf/Arabic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67cdd9d2744cf9d587f306a2728c835e51f54348
--- /dev/null
+++ b/vsfilter/libssf/Arabic.cpp
@@ -0,0 +1,327 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ *  The "ArabicForms" table originates from fontforge
+ *
+ */
+
+#include "stdafx.h"
+#include "Arabic.h"
+
+namespace ssf
+{
+	static const struct arabicforms 
+	{
+		unsigned short initial, medial, final, isolated;
+		unsigned int isletter: 1;
+		unsigned int joindual: 1;
+		unsigned int required_lig_with_alef: 1;
+	}
+	ArabicForms[256] = 
+	{
+		{ 0x0600, 0x0600, 0x0600, 0x0600, 0, 0, 0 },
+		{ 0x0601, 0x0601, 0x0601, 0x0601, 0, 0, 0 },
+		{ 0x0602, 0x0602, 0x0602, 0x0602, 0, 0, 0 },
+		{ 0x0603, 0x0603, 0x0603, 0x0603, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x060b, 0x060b, 0x060b, 0x060b, 0, 0, 0 },
+		{ 0x060c, 0x060c, 0x060c, 0x060c, 0, 0, 0 },
+		{ 0x060d, 0x060d, 0x060d, 0x060d, 0, 0, 0 },
+		{ 0x060e, 0x060e, 0x060e, 0x060e, 0, 0, 0 },
+		{ 0x060f, 0x060f, 0x060f, 0x060f, 0, 0, 0 },
+		{ 0x0610, 0x0610, 0x0610, 0x0610, 0, 0, 0 },
+		{ 0x0611, 0x0611, 0x0611, 0x0611, 0, 0, 0 },
+		{ 0x0612, 0x0612, 0x0612, 0x0612, 0, 0, 0 },
+		{ 0x0613, 0x0613, 0x0613, 0x0613, 0, 0, 0 },
+		{ 0x0614, 0x0614, 0x0614, 0x0614, 0, 0, 0 },
+		{ 0x0615, 0x0615, 0x0615, 0x0615, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x061b, 0x061b, 0x061b, 0x061b, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x061e, 0x061e, 0x061e, 0x061e, 0, 0, 0 },
+		{ 0x061f, 0x061f, 0x061f, 0x061f, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0621, 0x0621, 0x0621, 0xfe80, 1, 0, 0 },
+		{ 0x0622, 0x0622, 0xfe82, 0xfe81, 1, 0, 0 },
+		{ 0x0623, 0x0623, 0xfe84, 0xfe83, 1, 0, 0 },
+		{ 0x0624, 0x0624, 0xfe86, 0xfe85, 1, 0, 0 },
+		{ 0x0625, 0x0625, 0xfe88, 0xfe87, 1, 0, 0 },
+		{ 0xfe8b, 0xfe8c, 0xfe8a, 0xfe89, 1, 1, 0 },
+		{ 0x0627, 0x0627, 0xfe8e, 0xfe8d, 1, 0, 0 },
+		{ 0xfe91, 0xfe92, 0xfe90, 0xfe8f, 1, 1, 0 },
+		{ 0x0629, 0x0629, 0xfe94, 0xfe93, 1, 0, 0 },
+		{ 0xfe97, 0xfe98, 0xfe96, 0xfe95, 1, 1, 0 },
+		{ 0xfe9b, 0xfe9c, 0xfe9a, 0xfe99, 1, 1, 0 },
+		{ 0xfe9f, 0xfea0, 0xfe9e, 0xfe9d, 1, 1, 0 },
+		{ 0xfea3, 0xfea4, 0xfea2, 0xfea1, 1, 1, 0 },
+		{ 0xfea7, 0xfea8, 0xfea6, 0xfea5, 1, 1, 0 },
+		{ 0x062f, 0x062f, 0xfeaa, 0xfea9, 1, 0, 0 },
+		{ 0x0630, 0x0630, 0xfeac, 0xfeab, 1, 0, 0 },
+		{ 0x0631, 0x0631, 0xfeae, 0xfead, 1, 0, 0 },
+		{ 0x0632, 0x0632, 0xfeb0, 0xfeaf, 1, 0, 0 },
+		{ 0xfeb3, 0xfeb4, 0xfeb2, 0xfeb1, 1, 1, 0 },
+		{ 0xfeb7, 0xfeb8, 0xfeb6, 0xfeb5, 1, 1, 0 },
+		{ 0xfebb, 0xfebc, 0xfeba, 0xfeb9, 1, 1, 0 },
+		{ 0xfebf, 0xfec0, 0xfebe, 0xfebd, 1, 1, 0 },
+		{ 0xfec3, 0xfec4, 0xfec2, 0xfec1, 1, 1, 0 },
+		{ 0xfec7, 0xfec8, 0xfec6, 0xfec5, 1, 1, 0 },
+		{ 0xfecb, 0xfecc, 0xfeca, 0xfec9, 1, 1, 0 },
+		{ 0xfecf, 0xfed0, 0xfece, 0xfecd, 1, 1, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0640, 0x0640, 0x0640, 0x0640, 0, 0, 0 },
+		{ 0xfed3, 0xfed4, 0xfed2, 0xfed1, 1, 1, 0 },
+		{ 0xfed7, 0xfed8, 0xfed6, 0xfed5, 1, 1, 0 },
+		{ 0xfedb, 0xfedc, 0xfeda, 0xfed9, 1, 1, 0 },
+		{ 0xfedf, 0xfee0, 0xfede, 0xfedd, 1, 1, 1 },
+		{ 0xfee3, 0xfee4, 0xfee2, 0xfee1, 1, 1, 0 },
+		{ 0xfee7, 0xfee8, 0xfee6, 0xfee5, 1, 1, 0 },
+		{ 0xfeeb, 0xfeec, 0xfeea, 0xfee9, 1, 1, 0 },
+		{ 0x0648, 0x0648, 0xfeee, 0xfeed, 1, 0, 0 },
+		{ 0x0649, 0x0649, 0xfef0, 0xfeef, 1, 0, 0 },
+		{ 0xfef3, 0xfef4, 0xfef2, 0xfef1, 1, 1, 0 },
+		{ 0x064b, 0x064b, 0x064b, 0x064b, 0, 0, 0 },
+		{ 0x064c, 0x064c, 0x064c, 0x064c, 0, 0, 0 },
+		{ 0x064d, 0x064d, 0x064d, 0x064d, 0, 0, 0 },
+		{ 0x064e, 0x064e, 0x064e, 0x064e, 0, 0, 0 },
+		{ 0x064f, 0x064f, 0x064f, 0x064f, 0, 0, 0 },
+		{ 0x0650, 0x0650, 0x0650, 0x0650, 0, 0, 0 },
+		{ 0x0651, 0x0651, 0x0651, 0x0651, 0, 0, 0 },
+		{ 0x0652, 0x0652, 0x0652, 0x0652, 0, 0, 0 },
+		{ 0x0653, 0x0653, 0x0653, 0x0653, 0, 0, 0 },
+		{ 0x0654, 0x0654, 0x0654, 0x0654, 0, 0, 0 },
+		{ 0x0655, 0x0655, 0x0655, 0x0655, 0, 0, 0 },
+		{ 0x0656, 0x0656, 0x0656, 0x0656, 0, 0, 0 },
+		{ 0x0657, 0x0657, 0x0657, 0x0657, 0, 0, 0 },
+		{ 0x0658, 0x0658, 0x0658, 0x0658, 0, 0, 0 },
+		{ 0x0659, 0x0659, 0x0659, 0x0659, 0, 0, 0 },
+		{ 0x065a, 0x065a, 0x065a, 0x065a, 0, 0, 0 },
+		{ 0x065b, 0x065b, 0x065b, 0x065b, 0, 0, 0 },
+		{ 0x065c, 0x065c, 0x065c, 0x065c, 0, 0, 0 },
+		{ 0x065d, 0x065d, 0x065d, 0x065d, 0, 0, 0 },
+		{ 0x065e, 0x065e, 0x065e, 0x065e, 0, 0, 0 },
+		{ 0x0000, 0x0000, 0x0000, 0x0000, 0, 0, 0 },
+		{ 0x0660, 0x0660, 0x0660, 0x0660, 0, 0, 0 },
+		{ 0x0661, 0x0661, 0x0661, 0x0661, 0, 0, 0 },
+		{ 0x0662, 0x0662, 0x0662, 0x0662, 0, 0, 0 },
+		{ 0x0663, 0x0663, 0x0663, 0x0663, 0, 0, 0 },
+		{ 0x0664, 0x0664, 0x0664, 0x0664, 0, 0, 0 },
+		{ 0x0665, 0x0665, 0x0665, 0x0665, 0, 0, 0 },
+		{ 0x0666, 0x0666, 0x0666, 0x0666, 0, 0, 0 },
+		{ 0x0667, 0x0667, 0x0667, 0x0667, 0, 0, 0 },
+		{ 0x0668, 0x0668, 0x0668, 0x0668, 0, 0, 0 },
+		{ 0x0669, 0x0669, 0x0669, 0x0669, 0, 0, 0 },
+		{ 0x066a, 0x066a, 0x066a, 0x066a, 0, 0, 0 },
+		{ 0x066b, 0x066b, 0x066b, 0x066b, 0, 0, 0 },
+		{ 0x066c, 0x066c, 0x066c, 0x066c, 0, 0, 0 },
+		{ 0x066d, 0x066d, 0x066d, 0x066d, 0, 0, 0 },
+		{ 0x066e, 0x066e, 0x066e, 0x066e, 1, 0, 0 },
+		{ 0x066f, 0x066f, 0x066f, 0x066f, 1, 0, 0 },
+		{ 0x0670, 0x0670, 0x0670, 0x0670, 1, 0, 0 },
+		{ 0x0671, 0x0671, 0xfb51, 0xfb50, 1, 0, 0 },
+		{ 0x0672, 0x0672, 0x0672, 0x0672, 1, 0, 0 },
+		{ 0x0673, 0x0673, 0x0673, 0x0673, 1, 0, 0 },
+		{ 0x0674, 0x0674, 0x0674, 0x0674, 1, 0, 0 },
+		{ 0x0675, 0x0675, 0x0675, 0x0675, 1, 0, 0 },
+		{ 0x0676, 0x0676, 0x0676, 0x0676, 1, 0, 0 },
+		{ 0x0677, 0x0677, 0x0677, 0xfbdd, 1, 0, 0 },
+		{ 0x0678, 0x0678, 0x0678, 0x0678, 1, 0, 0 },
+		{ 0xfb68, 0xfb69, 0xfb67, 0xfb66, 1, 1, 0 },
+		{ 0xfb60, 0xfb61, 0xfb5f, 0xfb5e, 1, 1, 0 },
+		{ 0xfb54, 0xfb55, 0xfb53, 0xfb52, 1, 1, 0 },
+		{ 0x067c, 0x067c, 0x067c, 0x067c, 1, 0, 0 },
+		{ 0x067d, 0x067d, 0x067d, 0x067d, 1, 0, 0 },
+		{ 0xfb58, 0xfb59, 0xfb57, 0xfb56, 1, 1, 0 },
+		{ 0xfb64, 0xfb65, 0xfb63, 0xfb62, 1, 1, 0 },
+		{ 0xfb5c, 0xfb5d, 0xfb5b, 0xfb5a, 1, 1, 0 },
+		{ 0x0681, 0x0681, 0x0681, 0x0681, 1, 0, 0 },
+		{ 0x0682, 0x0682, 0x0682, 0x0682, 1, 0, 0 },
+		{ 0xfb78, 0xfb79, 0xfb77, 0xfb76, 1, 1, 0 },
+		{ 0xfb74, 0xfb75, 0xfb73, 0xfb72, 1, 1, 0 },
+		{ 0x0685, 0x0685, 0x0685, 0x0685, 1, 0, 0 },
+		{ 0xfb7c, 0xfb7d, 0xfb7b, 0xfb7a, 1, 1, 0 },
+		{ 0xfb80, 0xfb81, 0xfb7f, 0xfb7e, 1, 1, 0 },
+		{ 0x0688, 0x0688, 0xfb89, 0xfb88, 1, 0, 0 },
+		{ 0x0689, 0x0689, 0x0689, 0x0689, 1, 0, 0 },
+		{ 0x068a, 0x068a, 0x068a, 0x068a, 1, 0, 0 },
+		{ 0x068b, 0x068b, 0x068b, 0x068b, 1, 0, 0 },
+		{ 0x068c, 0x068c, 0xfb85, 0xfb84, 1, 0, 0 },
+		{ 0x068d, 0x068d, 0xfb83, 0xfb82, 1, 0, 0 },
+		{ 0x068e, 0x068e, 0xfb87, 0xfb86, 1, 0, 0 },
+		{ 0x068f, 0x068f, 0x068f, 0x068f, 1, 0, 0 },
+		{ 0x0690, 0x0690, 0x0690, 0x0690, 1, 0, 0 },
+		{ 0x0691, 0x0691, 0xfb8d, 0xfb8c, 1, 0, 0 },
+		{ 0x0692, 0x0692, 0x0692, 0x0692, 1, 0, 0 },
+		{ 0x0693, 0x0693, 0x0693, 0x0693, 1, 0, 0 },
+		{ 0x0694, 0x0694, 0x0694, 0x0694, 1, 0, 0 },
+		{ 0x0695, 0x0695, 0x0695, 0x0695, 1, 0, 0 },
+		{ 0x0696, 0x0696, 0x0696, 0x0696, 1, 0, 0 },
+		{ 0x0697, 0x0697, 0x0697, 0x0697, 1, 0, 0 },
+		{ 0x0698, 0x0698, 0xfb8b, 0xfb8a, 1, 0, 0 },
+		{ 0x0699, 0x0699, 0x0699, 0x0699, 1, 0, 0 },
+		{ 0x069a, 0x069a, 0x069a, 0x069a, 1, 0, 0 },
+		{ 0x069b, 0x069b, 0x069b, 0x069b, 1, 0, 0 },
+		{ 0x069c, 0x069c, 0x069c, 0x069c, 1, 0, 0 },
+		{ 0x069d, 0x069d, 0x069d, 0x069d, 1, 0, 0 },
+		{ 0x069e, 0x069e, 0x069e, 0x069e, 1, 0, 0 },
+		{ 0x069f, 0x069f, 0x069f, 0x069f, 1, 0, 0 },
+		{ 0x06a0, 0x06a0, 0x06a0, 0x06a0, 1, 0, 0 },
+		{ 0x06a1, 0x06a1, 0x06a1, 0x06a1, 1, 0, 0 },
+		{ 0x06a2, 0x06a2, 0x06a2, 0x06a2, 1, 0, 0 },
+		{ 0x06a3, 0x06a3, 0x06a3, 0x06a3, 1, 0, 0 },
+		{ 0xfb6c, 0xfb6d, 0xfb6b, 0xfb6a, 1, 1, 0 },
+		{ 0x06a5, 0x06a5, 0x06a5, 0x06a5, 1, 0, 0 },
+		{ 0xfb70, 0xfb71, 0xfb6f, 0xfb6e, 1, 1, 0 },
+		{ 0x06a7, 0x06a7, 0x06a7, 0x06a7, 1, 0, 0 },
+		{ 0x06a8, 0x06a8, 0x06a8, 0x06a8, 1, 0, 0 },
+		{ 0xfb90, 0xfb91, 0xfb8f, 0xfb8e, 1, 1, 0 },
+		{ 0x06aa, 0x06aa, 0x06aa, 0x06aa, 1, 0, 0 },
+		{ 0x06ab, 0x06ab, 0x06ab, 0x06ab, 1, 0, 0 },
+		{ 0x06ac, 0x06ac, 0x06ac, 0x06ac, 1, 0, 0 },
+		{ 0xfbd5, 0xfbd6, 0xfbd4, 0xfbd3, 1, 1, 0 },
+		{ 0x06ae, 0x06ae, 0x06ae, 0x06ae, 1, 0, 0 },
+		{ 0xfb94, 0xfb95, 0xfb93, 0xfb92, 1, 1, 0 },
+		{ 0x06b0, 0x06b0, 0x06b0, 0x06b0, 1, 0, 0 },
+		{ 0xfb9c, 0xfb9d, 0xfb9b, 0xfb9a, 1, 1, 0 },
+		{ 0x06b2, 0x06b2, 0x06b2, 0x06b2, 1, 0, 0 },
+		{ 0xfb98, 0xfb99, 0xfb97, 0xfb96, 1, 1, 0 },
+		{ 0x06b4, 0x06b4, 0x06b4, 0x06b4, 1, 0, 0 },
+		{ 0x06b5, 0x06b5, 0x06b5, 0x06b5, 1, 0, 0 },
+		{ 0x06b6, 0x06b6, 0x06b6, 0x06b6, 1, 0, 0 },
+		{ 0x06b7, 0x06b7, 0x06b7, 0x06b7, 1, 0, 0 },
+		{ 0x06b8, 0x06b8, 0x06b8, 0x06b8, 1, 0, 0 },
+		{ 0x06b9, 0x06b9, 0x06b9, 0x06b9, 1, 0, 0 },
+		{ 0x06ba, 0x06ba, 0xfb9f, 0xfb9e, 1, 0, 0 },
+		{ 0xfba2, 0xfba3, 0xfba1, 0xfba0, 1, 1, 0 },
+		{ 0x06bc, 0x06bc, 0x06bc, 0x06bc, 1, 0, 0 },
+		{ 0x06bd, 0x06bd, 0x06bd, 0x06bd, 1, 0, 0 },
+		{ 0xfbac, 0xfbad, 0xfbab, 0xfbaa, 1, 1, 0 },
+		{ 0x06bf, 0x06bf, 0x06bf, 0x06bf, 1, 0, 0 },
+		{ 0x06c0, 0x06c0, 0xfba5, 0xfba4, 1, 0, 0 },
+		{ 0xfba8, 0xfba9, 0xfba7, 0xfba6, 1, 1, 0 },
+		{ 0x06c2, 0x06c2, 0x06c2, 0x06c2, 1, 0, 0 },
+		{ 0x06c3, 0x06c3, 0x06c3, 0x06c3, 1, 0, 0 },
+		{ 0x06c4, 0x06c4, 0x06c4, 0x06c4, 1, 0, 0 },
+		{ 0x06c5, 0x06c5, 0xfbe1, 0xfbe0, 1, 0, 0 },
+		{ 0x06c6, 0x06c6, 0xfbda, 0xfbd9, 1, 0, 0 },
+		{ 0x06c7, 0x06c7, 0xfbd8, 0xfbd7, 1, 0, 0 },
+		{ 0x06c8, 0x06c8, 0xfbdc, 0xfbdb, 1, 0, 0 },
+		{ 0x06c9, 0x06c9, 0xfbe3, 0xfbe2, 1, 0, 0 },
+		{ 0x06ca, 0x06ca, 0x06ca, 0x06ca, 1, 0, 0 },
+		{ 0x06cb, 0x06cb, 0xfbdf, 0xfbde, 1, 0, 0 },
+		{ 0xfbfe, 0xfbff, 0xfbfd, 0xfbfc, 1, 1, 0 },
+		{ 0x06cd, 0x06cd, 0x06cd, 0x06cd, 1, 0, 0 },
+		{ 0x06ce, 0x06ce, 0x06ce, 0x06ce, 1, 0, 0 },
+		{ 0x06cf, 0x06cf, 0x06cf, 0x06cf, 1, 0, 0 },
+		{ 0xfbe6, 0xfbe7, 0xfbe5, 0xfbe4, 1, 1, 0 },
+		{ 0x06d1, 0x06d1, 0x06d1, 0x06d1, 1, 0, 0 },
+		{ 0x06d2, 0x06d2, 0xfbaf, 0xfbae, 1, 0, 0 },
+		{ 0x06d3, 0x06d3, 0xfbb1, 0xfbb0, 1, 0, 0 },
+		{ 0x06d4, 0x06d4, 0x06d4, 0x06d4, 0, 0, 0 },
+		{ 0x06d5, 0x06d5, 0x06d5, 0x06d5, 1, 0, 0 },
+		{ 0x06d6, 0x06d6, 0x06d6, 0x06d6, 0, 0, 0 },
+		{ 0x06d7, 0x06d7, 0x06d7, 0x06d7, 0, 0, 0 },
+		{ 0x06d8, 0x06d8, 0x06d8, 0x06d8, 0, 0, 0 },
+		{ 0x06d9, 0x06d9, 0x06d9, 0x06d9, 0, 0, 0 },
+		{ 0x06da, 0x06da, 0x06da, 0x06da, 0, 0, 0 },
+		{ 0x06db, 0x06db, 0x06db, 0x06db, 0, 0, 0 },
+		{ 0x06dc, 0x06dc, 0x06dc, 0x06dc, 0, 0, 0 },
+		{ 0x06dd, 0x06dd, 0x06dd, 0x06dd, 0, 0, 0 },
+		{ 0x06de, 0x06de, 0x06de, 0x06de, 0, 0, 0 },
+		{ 0x06df, 0x06df, 0x06df, 0x06df, 0, 0, 0 },
+		{ 0x06e0, 0x06e0, 0x06e0, 0x06e0, 0, 0, 0 },
+		{ 0x06e1, 0x06e1, 0x06e1, 0x06e1, 0, 0, 0 },
+		{ 0x06e2, 0x06e2, 0x06e2, 0x06e2, 0, 0, 0 },
+		{ 0x06e3, 0x06e3, 0x06e3, 0x06e3, 0, 0, 0 },
+		{ 0x06e4, 0x06e4, 0x06e4, 0x06e4, 0, 0, 0 },
+		{ 0x06e5, 0x06e5, 0x06e5, 0x06e5, 0, 0, 0 },
+		{ 0x06e6, 0x06e6, 0x06e6, 0x06e6, 0, 0, 0 },
+		{ 0x06e7, 0x06e7, 0x06e7, 0x06e7, 0, 0, 0 },
+		{ 0x06e8, 0x06e8, 0x06e8, 0x06e8, 0, 0, 0 },
+		{ 0x06e9, 0x06e9, 0x06e9, 0x06e9, 0, 0, 0 },
+		{ 0x06ea, 0x06ea, 0x06ea, 0x06ea, 0, 0, 0 },
+		{ 0x06eb, 0x06eb, 0x06eb, 0x06eb, 0, 0, 0 },
+		{ 0x06ec, 0x06ec, 0x06ec, 0x06ec, 0, 0, 0 },
+		{ 0x06ed, 0x06ed, 0x06ed, 0x06ed, 0, 0, 0 },
+		{ 0x06ee, 0x06ee, 0x06ee, 0x06ee, 1, 0, 0 },
+		{ 0x06ef, 0x06ef, 0x06ef, 0x06ef, 1, 0, 0 },
+		{ 0x06f0, 0x06f0, 0x06f0, 0x06f0, 0, 0, 0 },
+		{ 0x06f1, 0x06f1, 0x06f1, 0x06f1, 0, 0, 0 },
+		{ 0x06f2, 0x06f2, 0x06f2, 0x06f2, 0, 0, 0 },
+		{ 0x06f3, 0x06f3, 0x06f3, 0x06f3, 0, 0, 0 },
+		{ 0x06f4, 0x06f4, 0x06f4, 0x06f4, 0, 0, 0 },
+		{ 0x06f5, 0x06f5, 0x06f5, 0x06f5, 0, 0, 0 },
+		{ 0x06f6, 0x06f6, 0x06f6, 0x06f6, 0, 0, 0 },
+		{ 0x06f7, 0x06f7, 0x06f7, 0x06f7, 0, 0, 0 },
+		{ 0x06f8, 0x06f8, 0x06f8, 0x06f8, 0, 0, 0 },
+		{ 0x06f9, 0x06f9, 0x06f9, 0x06f9, 0, 0, 0 },
+		{ 0x06fa, 0x06fa, 0x06fa, 0x06fa, 1, 0, 0 },
+		{ 0x06fb, 0x06fb, 0x06fb, 0x06fb, 1, 0, 0 },
+		{ 0x06fc, 0x06fc, 0x06fc, 0x06fc, 1, 0, 0 },
+		{ 0x06fd, 0x06fd, 0x06fd, 0x06fd, 0, 0, 0 },
+		{ 0x06fe, 0x06fe, 0x06fe, 0x06fe, 0, 0, 0 },
+		{ 0x06ff, 0x06ff, 0x06ff, 0x06ff, 1, 0, 0 }
+	};
+
+	bool Arabic::IsArabic(WCHAR c)
+	{
+		return c >= 0x600 && c <= 0x6ff;
+	}
+
+	bool Arabic::Replace(WCHAR& c, pres_form_t pf)
+	{
+		if(!IsArabic(c)) return false;
+
+		const arabicforms& af = ArabicForms[c - 0x600];
+
+		switch(pf)
+		{
+		case isol: c = af.isolated; break;
+		case init: c = af.initial; break;
+		case medi: c = af.medial; break;
+		case fina: c = af.final; break;
+		}
+
+		return true;
+	}
+
+	bool Arabic::Replace(WCHAR& c, WCHAR prev, WCHAR next)
+	{
+		if(!IsArabic(c)) return false;
+
+		bool p = IsArabic(prev);
+		bool n = IsArabic(next);
+
+		return Replace(c, p ? (n ? medi : fina) : (n ? init : isol));
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Arabic.h b/vsfilter/libssf/Arabic.h
new file mode 100644
index 0000000000000000000000000000000000000000..b761b28c1b616d31e70b75d0b4e9e43a35b300d6
--- /dev/null
+++ b/vsfilter/libssf/Arabic.h
@@ -0,0 +1,34 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	class Arabic
+	{
+	public:
+		enum pres_form_t {isol, init, medi, fina};
+		static bool IsArabic(WCHAR c);
+		static bool Replace(WCHAR& c, pres_form_t pf);
+		static bool Replace(WCHAR& c, WCHAR prev, WCHAR next);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Array.cpp b/vsfilter/libssf/Array.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39150114366d81d64ff52b53e94fdea26d3172b6
--- /dev/null
+++ b/vsfilter/libssf/Array.cpp
@@ -0,0 +1,28 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Array.h"
+
+namespace ssf
+{
+
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Array.h b/vsfilter/libssf/Array.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ca4e8918ba68def6318103fc1c2b94990d29677
--- /dev/null
+++ b/vsfilter/libssf/Array.h
@@ -0,0 +1,108 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	// simple array class for simple types without constructors, 
+	// and it doesn't free its reserves on SetCount(0)
+
+	template<class T> 
+	class Array
+	{
+		T* m_pData;
+		size_t m_nSize;
+		size_t m_nMaxSize;
+		size_t m_nGrowBy;
+
+	public:
+		Array() {m_pData = NULL; m_nSize = m_nMaxSize = 0; m_nGrowBy = 4096;}
+		virtual ~Array() {if(m_pData) _aligned_free(m_pData);}
+
+		void SetCount(size_t nSize, size_t nGrowBy = 0)
+		{
+			if(nGrowBy > 0)
+			{
+				m_nGrowBy = nGrowBy;
+			}
+
+			if(nSize > m_nMaxSize)
+			{
+				m_nMaxSize = nSize + max(m_nGrowBy, m_nSize);
+				size_t nBytes = m_nMaxSize * sizeof(T);
+				m_pData = m_pData ? (T*)_aligned_realloc(m_pData, nBytes, 16) : (T*)_aligned_malloc(nBytes, 16);
+			}
+
+			m_nSize = nSize;
+		}
+
+		size_t GetCount() const {return m_nSize;}
+
+		void RemoveAll() {m_nSize = 0;}
+		bool IsEmpty() const {return m_nSize == 0;}
+
+		T* GetData() {return m_pData;}
+
+		void Add(const T& t)
+		{
+			size_t nPos = m_nSize;
+			SetCount(m_nSize+1);
+			m_pData[nPos] = t;
+		}
+
+		void Append(const Array& a, size_t nGrowBy = 0)
+		{
+			Append(a.m_pData, a.m_nSize, nGrowBy);
+		}
+
+		void Append(const T* ptr, size_t nSize, size_t nGrowBy = 0)
+		{
+			if(!nSize) return;
+			size_t nOldSize = m_nSize;
+			SetCount(nOldSize + nSize);
+			memcpy(m_pData + nOldSize, ptr, nSize * sizeof(T));
+		}
+
+		const T& operator [] (size_t i) const {return m_pData[i];}
+		T& operator [] (size_t i) {return m_pData[i];}
+
+		void Copy(const Array& v)
+		{
+			SetCount(v.GetCount());
+			memcpy(m_pData, v.m_pData, m_nSize * sizeof(T));
+		}
+
+		void Move(Array& v)
+		{
+			Swap(v);
+			v.SetCount(0);
+		}
+
+		void Swap(Array& v)
+		{
+			T* pData = m_pData; m_pData = v.m_pData; v.m_pData = pData;
+			size_t nSize = m_nSize; m_nSize = v.m_nSize; v.m_nSize = nSize;
+			size_t nMaxSize = m_nMaxSize; m_nMaxSize = v.m_nMaxSize; v.m_nMaxSize = nMaxSize;
+			size_t nGrowBy = m_nGrowBy; m_nGrowBy = v.m_nGrowBy; v.m_nGrowBy = nGrowBy;
+		}
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Exception.cpp b/vsfilter/libssf/Exception.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d597aab60e5db2bc99f6db48b408708f4d2f391
--- /dev/null
+++ b/vsfilter/libssf/Exception.cpp
@@ -0,0 +1,35 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Exception.h"
+
+namespace ssf
+{
+	Exception::Exception(LPCTSTR fmt, ...) 
+	{
+		va_list args;
+		va_start(args, fmt);
+		int len = _vsctprintf(fmt, args) + 1;
+		if(len > 0) _vstprintf_s(m_msg.GetBufferSetLength(len), len, fmt, args);
+		va_end(args);
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Exception.h b/vsfilter/libssf/Exception.h
new file mode 100644
index 0000000000000000000000000000000000000000..6cf1f03f59b557bbb30510b2ba08c4368f6687d2
--- /dev/null
+++ b/vsfilter/libssf/Exception.h
@@ -0,0 +1,35 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	class Exception
+	{
+		CString m_msg;
+
+	public:
+		Exception(LPCTSTR fmt, ...);
+		
+		CString ToString() {return m_msg;}
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/File.cpp b/vsfilter/libssf/File.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99fdf0c0769cd1d7c9f7d9bfc44dcf8cea9871b0
--- /dev/null
+++ b/vsfilter/libssf/File.cpp
@@ -0,0 +1,261 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "File.h"
+
+#ifndef iswcsym
+#define iswcsym(c) (iswalnum(c) || (c) == '_')
+#endif
+
+namespace ssf
+{
+	File::File()
+	{
+	}
+
+	File::~File()
+	{
+	}
+
+	void File::Parse(InputStream& s, LPCWSTR predef)
+	{
+		Reference* pRef = CreateRootRef();
+
+		SetPredefined(true);
+
+		try {ParseDefs(WCharInputStream(predef), pRef);}
+		catch(Exception& e) {ASSERT(0); TRACE(_T("%s\n"), e.ToString());}
+
+		SetPredefined(false);
+
+		ParseDefs(s, pRef);
+
+		Commit();
+
+		if(s.PeekChar() != Stream::EOS)
+		{
+			TRACE(_T("Warning: parsing ended before EOF!\n"));
+		}
+	}
+
+	void File::ParseDefs(InputStream& s, Reference* pParentRef)
+	{
+		while(s.SkipWhiteSpace(L";") != '}' && s.PeekChar() != Stream::EOS)
+		{
+			NodePriority priority = PNormal;
+			CAtlList<CStringW> types;
+			CStringW name;
+
+			int c = s.SkipWhiteSpace();
+
+			if(c == '*') {s.GetChar(); priority = PLow;}
+			else if(c == '!') {s.GetChar(); priority = PHigh;}
+
+			ParseTypes(s, types);
+
+			if(s.SkipWhiteSpace() == '#')
+			{
+				s.GetChar();
+				ParseName(s, name);
+			}
+
+			if(types.IsEmpty())
+			{
+				if(name.IsEmpty()) s.ThrowError(_T("syntax error"));
+				types.AddTail(L"?");
+			}
+
+			Reference* pRef = pParentRef;
+
+			while(types.GetCount() > 1)
+				pRef = CreateRef(CreateDef(pRef, types.RemoveHead()));
+
+			Definition* pDef = NULL;
+
+			if(!types.IsEmpty())
+				pDef = CreateDef(pRef, types.RemoveHead(), name, priority);
+
+			c = s.SkipWhiteSpace(L":=");
+
+			if(c == '"' || c == '\'') ParseQuotedString(s, pDef);
+			else if(iswdigit(c) || c == '+' || c == '-') ParseNumber(s, pDef);
+			else if(pDef->IsType(L"@")) ParseBlock(s, pDef);
+			else ParseRefs(s, pDef);
+		}
+
+		s.GetChar();
+	}
+
+	void File::ParseTypes(InputStream& s, CAtlList<CStringW>& types)
+	{
+		types.RemoveAll();
+
+		CStringW str;
+
+		for(int c = s.SkipWhiteSpace(); iswcsym(c) || c == '.' || c == '@'; c = s.PeekChar())
+		{
+			c = s.GetChar();
+
+			if(c == '.') 
+			{
+				if(str.IsEmpty()) s.ThrowError(_T("'type' cannot be an empty string"));
+				if(!iswcsym(s.PeekChar())) s.ThrowError(_T("unexpected dot after type '%s'"), CString(str));
+
+				types.AddTail(str);
+				str.Empty();
+			}
+			else
+			{
+				if(str.IsEmpty() && iswdigit(c)) s.ThrowError(_T("'type' cannot start with a number"));
+				if((!str.IsEmpty() || !types.IsEmpty()) && c == '@') s.ThrowError(_T("unexpected @ in 'type'"));
+
+				str += (WCHAR)c;
+			}
+		}
+
+		if(!str.IsEmpty())
+		{
+			types.AddTail(str);
+		}
+	}
+
+	void File::ParseName(InputStream& s, CStringW& name)
+	{
+		name.Empty();
+
+		for(int c = s.SkipWhiteSpace(); iswcsym(c); c = s.PeekChar())
+		{
+			if(name.IsEmpty() && iswdigit(c)) s.ThrowError(_T("'name' cannot start with a number"));
+			name += (WCHAR)s.GetChar();
+		}
+	}
+
+	void File::ParseQuotedString(InputStream& s, Definition* pDef)
+	{
+		CStringW v;
+
+		int quote = s.SkipWhiteSpace();
+		if(quote != '"' && quote != '\'') s.ThrowError(_T("expected qouted string"));
+		s.GetChar();
+
+		for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
+		{
+			c = s.GetChar();
+			if(c == quote) {pDef->SetAsValue(Definition::string, v); return;}
+			if(c == '\n') s.ThrowError(_T("qouted string terminated unexpectedly by a new-line character"));
+			if(c == '\\') c = s.GetChar();
+			if(c == Stream::EOS) s.ThrowError(_T("qouted string terminated unexpectedly by EOS"));
+			v += (WCHAR)c;
+		}
+
+		s.ThrowError(_T("unterminated quoted string"));
+	}
+
+	void File::ParseNumber(InputStream& s, Definition* pDef)
+	{
+		CStringW v, u;
+
+		for(int c = s.SkipWhiteSpace(); iswxdigit(c) || wcschr(L"+-.x:", c); c = s.PeekChar())
+		{
+			if((c == '+' || c == '-') && !v.IsEmpty()
+			|| (c == '.' && (v.IsEmpty() || v.Find('.') >= 0 || v.Find('x') >= 0))
+			|| (c == 'x' && v != '0')
+			|| (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F') && v.Find(L"0x") != 0
+			|| (c == ':' && v.IsEmpty()))
+			{
+				s.ThrowError(_T("unexpected character '%c' in number"), (TCHAR)c);
+			}
+
+			v += (WCHAR)s.GetChar();
+		}
+
+		if(v.IsEmpty()) s.ThrowError(_T("invalid number"));
+
+		for(int c = s.SkipWhiteSpace(); iswcsym(c); c = s.PeekChar())
+		{
+			u += (WCHAR)s.GetChar();
+		}
+
+		pDef->SetAsNumber(v, u);
+	}
+
+	void File::ParseBlock(InputStream& s, Definition* pDef)
+	{
+		CStringW v;
+
+		int c = s.SkipWhiteSpace(L":=");
+		if(c != '{') s.ThrowError(_T("expected '{'"));
+		s.GetChar();
+
+		int depth = 0;
+
+		for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
+		{
+			c = s.GetChar();
+			if(c == '}' && depth == 0) {pDef->SetAsValue(Definition::block, v); return;}
+			if(c == '\\') {v += (WCHAR)c; c = s.GetChar();}
+			else if(c == '{') depth++;
+			else if(c == '}') depth--;
+			if(c == Stream::EOS) s.ThrowError(_T("block terminated unexpectedly by EOS"));
+			v += (WCHAR)c;
+		}
+
+		s.ThrowError(_T("unterminated block"));
+	}
+
+	void File::ParseRefs(InputStream& s, Definition* pParentDef, LPCWSTR term)
+	{
+		int c = s.SkipWhiteSpace();
+
+		do
+		{
+			if(pParentDef->IsValue()) s.ThrowError(_T("cannot mix references with other values"));
+
+			if(c == '{')
+			{
+				s.GetChar();
+				ParseDefs(s, CreateRef(pParentDef));
+			}
+			else if(iswcsym(c))
+			{
+				CStringW str;
+				ParseName(s, str);
+
+				// TODO: allow spec references: parent.<type>, self.<type>, child.<type>
+
+				Definition* pDef = GetDefByName(str);
+				if(!pDef) s.ThrowError(_T("cannot find definition of '%s'"), CString(str));
+
+				if(!pParentDef->IsVisible(pDef)) s.ThrowError(_T("cannot access '%s' from here"), CString(str));
+
+				pParentDef->AddTail(pDef);
+			}
+			else if(!wcschr(term, c) && c != Stream::EOS)
+			{
+				s.ThrowError(_T("unexpected character '%c'"), (TCHAR)c);
+			}
+
+			c = s.SkipWhiteSpace();
+		}
+		while(!wcschr(term, c) && c != Stream::EOS);
+	}
+}
diff --git a/vsfilter/libssf/File.h b/vsfilter/libssf/File.h
new file mode 100644
index 0000000000000000000000000000000000000000..1a44379ba452f0f632057ffca0f213711b28e43c
--- /dev/null
+++ b/vsfilter/libssf/File.h
@@ -0,0 +1,45 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "Stream.h"
+#include "NodeFactory.h"
+
+namespace ssf
+{
+	class File : public NodeFactory
+	{
+	public:
+		File();
+		virtual ~File();
+
+		void Parse(InputStream& s, LPCWSTR predef = NULL);
+
+		void ParseDefs(InputStream& s, Reference* pParentRef);
+		void ParseTypes(InputStream& s, CAtlList<CStringW>& types);
+		void ParseName(InputStream& s, CStringW& name);
+		void ParseQuotedString(InputStream& s, Definition* pDef);
+		void ParseNumber(InputStream& s, Definition* pDef);
+		void ParseBlock(InputStream& s, Definition* pDef);
+		void ParseRefs(InputStream& s, Definition* pParentDef, LPCWSTR term = L";}]");
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/FontWrapper.cpp b/vsfilter/libssf/FontWrapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b83fd6ee8a759e23c547adc8e628fcce24e12442
--- /dev/null
+++ b/vsfilter/libssf/FontWrapper.cpp
@@ -0,0 +1,57 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "FontWrapper.h"
+
+namespace ssf
+{
+	FontWrapper::FontWrapper(HDC hDC, HFONT hFont, const CStringW& key)
+		: m_hFont(hFont)
+		, m_key(key)
+	{
+		HFONT hFontOld = SelectFont(hDC, hFont);
+
+		GetTextMetrics(hDC, &m_tm);
+
+		if(DWORD nNumPairs = GetKerningPairs(hDC, 0, NULL))
+		{
+			KERNINGPAIR* kp = new KERNINGPAIR[nNumPairs];
+			GetKerningPairs(hDC, nNumPairs, kp);
+			for(DWORD i = 0; i < nNumPairs; i++) 
+				m_kerning[(kp[i].wFirst<<16)|kp[i].wSecond] = kp[i].iKernAmount;
+			delete [] kp;
+		}
+
+		SelectFont(hDC, hFontOld);
+	}
+
+	FontWrapper::~FontWrapper()
+	{
+		DeleteFont(m_hFont);
+	}
+
+	int FontWrapper::GetKernAmount(WCHAR c1, WCHAR c2)
+	{
+		int size = 0;
+		return m_kerning.Lookup((c1<<16)|c2, size) ? size : 0;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/FontWrapper.h b/vsfilter/libssf/FontWrapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..49b149c2cd6e74e9e3b99b0267237765d7672d0e
--- /dev/null
+++ b/vsfilter/libssf/FontWrapper.h
@@ -0,0 +1,41 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	class FontWrapper
+	{
+		HFONT m_hFont;
+		CStringW m_key;
+		TEXTMETRIC m_tm;
+		CAtlMap<DWORD, int> m_kerning;
+
+	public:
+		FontWrapper(HDC hDC, HFONT hFont, const CStringW& key);
+		virtual ~FontWrapper();
+		operator HFONT() const {return m_hFont;}
+		operator LPCWSTR() const {return m_key;}
+		const TEXTMETRIC& GetTextMetric() {return m_tm;}
+		int GetKernAmount(WCHAR c1, WCHAR c2);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Glyph.cpp b/vsfilter/libssf/Glyph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4bc0947484f905051a91ef3ad36a78dc4e4254af
--- /dev/null
+++ b/vsfilter/libssf/Glyph.cpp
@@ -0,0 +1,306 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Glyph.h"
+#include "Split.h"
+
+#define deg2rad(d) (float)(M_PI/180*(d))
+
+namespace ssf
+{
+	Glyph::Glyph()
+	{
+		c = 0;
+		font = NULL;
+		ascent = descent = width = spacing = fill = 0;
+		tl.x = tl.y = tls.x = tls.y = 0;
+	}
+
+	float Glyph::GetBackgroundSize() const
+	{
+		return style.background.size * (scale.cx + scale.cy) / 2;
+	}
+
+	float Glyph::GetShadowDepth() const
+	{
+		return style.shadow.depth * (scale.cx + scale.cy) / 2;
+	}
+
+	CRect Glyph::GetClipRect() const
+	{
+		CRect r = bbox + tl;
+
+		int size = (int)(GetBackgroundSize() + 0.5);
+		int depth = (int)(GetShadowDepth() + 0.5);
+
+		r.InflateRect(size, size);
+		r.InflateRect(depth, depth);
+
+		r.left >>= 6;
+		r.top >>= 6;
+		r.right = (r.right + 32) >> 6;
+		r.bottom = (r.bottom + 32) >> 6;
+
+		return r;
+	}
+
+	void Glyph::CreateBkg()
+	{
+		path_bkg.RemoveAll();
+
+		if(style.background.type == L"enlarge" && style.background.size > 0)
+		{
+			path_bkg.Enlarge(path, GetBackgroundSize());
+		}
+		else if(style.background.type == L"box" && style.background.size >= 0)
+		{
+			if(c != ssf::Text::LSEP)
+			{
+				int s = (int)(GetBackgroundSize() + 0.5);
+				int x0 = (!vertical ? -spacing/2 : ascent - row_ascent);
+				int y0 = (!vertical ? ascent - row_ascent : -spacing/2);
+				int x1 = x0 + (!vertical ? width + spacing : row_ascent + row_descent);
+				int y1 = y0 + (!vertical ? row_ascent + row_descent : width + spacing);
+				path_bkg.types.SetCount(4);
+				path_bkg.types[0] = PT_MOVETO;
+				path_bkg.types[1] = PT_LINETO;
+				path_bkg.types[2] = PT_LINETO;
+				path_bkg.types[3] = PT_LINETO;
+				path_bkg.points.SetCount(4);
+				path_bkg.points[0] = CPoint(x0-s, y0-s);
+				path_bkg.points[1] = CPoint(x1+s, y0-s);
+				path_bkg.points[2] = CPoint(x1+s, y1+s);
+				path_bkg.points[3] = CPoint(x0-s, y1+s);
+			}
+		}
+	}
+
+	void Glyph::CreateSplineCoeffs(const CRect& spdrc)
+	{
+		spline.RemoveAll();
+
+		if(style.placement.path.IsEmpty())
+			return;
+
+		size_t i = 0, j = style.placement.path.GetCount();
+
+		CAtlArray<Point> pts;
+		pts.SetCount(j + 2);
+
+		Point p;
+
+		while(i < j)
+		{
+			p.x = style.placement.path[i].x * scale.cx + spdrc.left * 64;
+			p.y = style.placement.path[i].y * scale.cy + spdrc.top * 64;
+			pts[++i] = p;
+		}
+
+		if(pts.GetCount() >= 4)
+		{
+			if(pts[1].x == pts[j].x && pts[1].y == pts[j].y)
+			{
+				pts.SetAt(0, pts[j-1]);
+				pts.SetAt(j+1, pts[2]);
+			}
+			else
+			{
+				p.x = pts[1].x*2 - pts[2].x;
+				p.y = pts[1].y*2 - pts[2].y;
+				pts.SetAt(0, p);
+
+				p.x = pts[j].x*2 - pts[j-1].x;
+				p.y = pts[j].y*2 - pts[j-1].y;
+				pts.SetAt(j+1, p);
+			}
+
+			spline.SetCount(pts.GetCount()-3);
+
+			for(size_t i = 0, j = pts.GetCount()-4; i <= j; i++)
+			{
+				static const float _1div6 = 1.0f / 6;
+
+				SplineCoeffs sc;
+
+				sc.cx[3] = _1div6*(-  pts[i+0].x + 3*pts[i+1].x - 3*pts[i+2].x + pts[i+3].x);
+				sc.cx[2] = _1div6*( 3*pts[i+0].x - 6*pts[i+1].x + 3*pts[i+2].x);
+				sc.cx[1] = _1div6*(-3*pts[i+0].x	            + 3*pts[i+2].x);
+				sc.cx[0] = _1div6*(   pts[i+0].x + 4*pts[i+1].x + 1*pts[i+2].x);
+
+				sc.cy[3] = _1div6*(-  pts[i+0].y + 3*pts[i+1].y - 3*pts[i+2].y + pts[i+3].y);
+				sc.cy[2] = _1div6*( 3*pts[i+0].y - 6*pts[i+1].y + 3*pts[i+2].y);
+				sc.cy[1] = _1div6*(-3*pts[i+0].y                + 3*pts[i+2].y);
+				sc.cy[0] = _1div6*(   pts[i+0].y + 4*pts[i+1].y + 1*pts[i+2].y);
+
+				spline.SetAt(i, sc);
+			}
+		}
+	}
+
+	void Glyph::Transform(GlyphPath& path, CPoint org, const CRect& subrect)
+	{
+		// TODO: add sse code path
+
+		float sx = style.font.scale.cx;
+		float sy = style.font.scale.cy;
+
+		bool brotate = style.placement.angle.x || style.placement.angle.y || style.placement.angle.z;
+		bool bspline = !spline.IsEmpty();
+		bool bscale = brotate || bspline || sx != 1 || sy != 1;
+
+		float caz = cos(deg2rad(style.placement.angle.z));
+		float saz = sin(deg2rad(style.placement.angle.z));
+		float cax = cos(deg2rad(style.placement.angle.x));
+		float sax = sin(deg2rad(style.placement.angle.x));
+		float cay = cos(deg2rad(style.placement.angle.y));
+		float say = sin(deg2rad(style.placement.angle.y));
+
+		for(size_t i = 0, j = path.types.GetCount(); i < j; i++)
+		{
+			CPoint p = path.points[i];
+
+			if(bscale)
+			{
+				float x, y, z, xx, yy, zz;
+
+				x = sx * (p.x - org.x);
+				y = sy * (p.y - org.y);
+				z = 0;
+
+				if(bspline)
+				{
+					float pos = vertical ? y + org.y + tl.y - subrect.top : x + org.x + tl.x - subrect.left;
+					float size = vertical ? subrect.Size().cy : subrect.Size().cx;
+					float dist = vertical ? x : y;
+
+					const SplineCoeffs* sc;
+					float t;
+
+					if(pos >= size)
+					{
+						sc = &spline[spline.GetCount() - 1];
+						t = 1;
+					}
+					else
+					{
+						float u = size / spline.GetCount();
+						sc = &spline[max((int)(pos / u), 0)];
+						t = fmod(pos, u) / u;
+					}
+
+					float nx = sc->cx[1] + 2*t*sc->cx[2] + 3*t*t*sc->cx[3];
+					float ny = sc->cy[1] + 2*t*sc->cy[2] + 3*t*t*sc->cy[3];
+					float nl = 1.0f / sqrt(nx*nx + ny*ny);
+
+					nx *= nl;
+					ny *= nl;
+
+					x = sc->cx[0] + t*(sc->cx[1] + t*(sc->cx[2] + t*sc->cx[3])) - ny * dist - org.x - tl.x;
+					y = sc->cy[0] + t*(sc->cy[1] + t*(sc->cy[2] + t*sc->cy[3])) + nx * dist - org.y - tl.y;
+				}
+
+				if(brotate)
+				{
+					xx = x*caz + y*saz;
+					yy = -(x*saz - y*caz);
+					zz = z;
+
+					x = xx;
+					y = yy*cax + zz*sax;
+					z = yy*sax - zz*cax;
+
+					xx = x*cay + z*say;
+					yy = y;
+					zz = x*say - z*cay;
+
+					zz = 1.0f / (max(zz, -19000) + 20000);
+
+					x = (xx * 20000) * zz;
+					y = (yy * 20000) * zz;
+				}
+
+				p.x = (int)(x + org.x + 0.5);
+				p.y = (int)(y + org.y + 0.5);
+
+				path.points[i] = p;
+			}
+
+			if(p.x < bbox.left) bbox.left = p.x;
+			if(p.x > bbox.right) bbox.right = p.x;
+			if(p.y < bbox.top) bbox.top = p.y;
+			if(p.y > bbox.bottom) bbox.bottom = p.y;
+		}
+	}
+
+	void Glyph::Transform(CPoint org, const CRect& subrect)
+	{
+		if(!style.placement.org.auto_x) org.x = style.placement.org.x * scale.cx;
+		if(!style.placement.org.auto_y) org.y = style.placement.org.y * scale.cy;
+
+		org -= tl;
+
+		bbox.SetRect(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
+
+		Transform(path_bkg, org, subrect);
+		Transform(path, org, subrect);
+
+		bbox |= CRect(0, 0, 0, 0);
+	}
+
+	void Glyph::Rasterize()
+	{
+		if(!path_bkg.IsEmpty())
+		{
+			ras_bkg.ScanConvert(path_bkg, bbox);
+			ras_bkg.Rasterize(tl.x, tl.y);
+		}
+
+		ras.ScanConvert(path, bbox);
+
+		if(style.background.type == L"outline" && style.background.size > 0)
+		{
+			ras.CreateWidenedRegion((int)(GetBackgroundSize() + 0.5));
+		}
+
+		//
+
+		Rasterizer* r = path_bkg.IsEmpty() ? &ras : &ras_bkg;
+		int plane = path_bkg.IsEmpty() ? (style.font.color.a < 255 ? 2 : 1) : 0;
+		
+		ras.Rasterize(tl.x, tl.y);
+		r->Blur(style.background.blur, plane);
+
+		if(style.shadow.depth > 0)
+		{
+			ras_shadow.Reuse(*r);
+
+			float depth = GetShadowDepth();
+
+			tls.x = tl.x + (int)(depth * cos(deg2rad(style.shadow.angle)) + 0.5);
+			tls.y = tl.y + (int)(depth * -sin(deg2rad(style.shadow.angle)) + 0.5);
+
+			ras_shadow.Rasterize(tls.x, tls.y);
+			ras_shadow.Blur(style.shadow.blur, plane ? 1 : 0);
+		}
+	}
+
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Glyph.h b/vsfilter/libssf/Glyph.h
new file mode 100644
index 0000000000000000000000000000000000000000..b65e6a888a7f9c6286bd824fa511f906edcfcfa2
--- /dev/null
+++ b/vsfilter/libssf/Glyph.h
@@ -0,0 +1,63 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include "GlyphPath.h"
+#include "FontWrapper.h"
+#include "Rasterizer.h"
+
+namespace ssf
+{
+	class Glyph
+	{
+		void Transform(GlyphPath& path, CPoint org, const CRect& subrect);
+
+		struct SplineCoeffs {float cx[4], cy[4];};
+
+	public:
+		WCHAR c;
+		Style style;
+		CAtlArray<SplineCoeffs> spline;
+		Size scale;
+		bool vertical;
+		FontWrapper* font;
+		int ascent, descent, width, spacing, fill;
+		int row_ascent, row_descent;
+		GlyphPath path, path_bkg;
+		CRect bbox;
+		CPoint tl, tls;
+		Rasterizer ras, ras_bkg, ras_shadow;
+
+	public:
+		Glyph();
+
+		void CreateBkg();
+		void CreateSplineCoeffs(const CRect& spdrc);
+		void Transform(CPoint org, const CRect& subrect);
+		void Rasterize();
+
+		float GetBackgroundSize() const;
+		float GetShadowDepth() const;
+		CRect GetClipRect() const;
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/GlyphPath.cpp b/vsfilter/libssf/GlyphPath.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eea762b3b95237933265ef85354a1778aa938dfd
--- /dev/null
+++ b/vsfilter/libssf/GlyphPath.cpp
@@ -0,0 +1,164 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "GlyphPath.h"
+
+namespace ssf
+{
+	GlyphPath::GlyphPath(const GlyphPath& path)
+	{
+		*this = path;
+	}
+
+	void GlyphPath::operator = (const GlyphPath& path)
+	{
+		types.Copy(path.types);
+		points.Copy(path.points);
+	}
+
+	bool GlyphPath::IsEmpty()
+	{
+		return types.IsEmpty() || points.IsEmpty();
+	}
+
+	void GlyphPath::RemoveAll()
+	{
+		types.RemoveAll();
+		points.RemoveAll();
+	}
+
+	void GlyphPath::MovePoints(const CPoint& o)
+	{
+		size_t n = points.GetCount();
+		POINT* p = points.GetData();
+
+		unsigned int i = 0;
+
+		if(!!(g_cpuid.m_flags & CCpuID::sse2) && !((DWORD_PTR)p & 7))
+		{
+			for( ; i < n && ((DWORD_PTR)&p[i] & 15); i++)
+			{
+				p[i].x += o.x;
+				p[i].y += o.y;
+			}
+
+			__m128i oo = _mm_set_epi32(o.y, o.x, o.y, o.x);
+
+			for(unsigned int j = i + ((n - i) & ~7); i < j; i += 8)
+			{
+				__m128i r0 = _mm_load_si128((__m128i*)&p[i+0]);
+				__m128i r1 = _mm_load_si128((__m128i*)&p[i+2]);
+				__m128i r2 = _mm_load_si128((__m128i*)&p[i+4]);
+				__m128i r3 = _mm_load_si128((__m128i*)&p[i+6]);
+				_mm_store_si128((__m128i*)&p[i+0], _mm_add_epi32(r0, oo));
+				_mm_store_si128((__m128i*)&p[i+2], _mm_add_epi32(r1, oo));
+				_mm_store_si128((__m128i*)&p[i+4], _mm_add_epi32(r2, oo));
+				_mm_store_si128((__m128i*)&p[i+6], _mm_add_epi32(r3, oo));
+			}
+		}
+
+		for(; i < n; i++)
+		{
+			p[i].x += o.x;
+			p[i].y += o.y;
+		}
+	}
+
+	void GlyphPath::Enlarge(const GlyphPath& src, float size)
+	{
+		types.SetCount(src.types.GetCount());
+		points.SetCount(src.points.GetCount());
+
+		memcpy(types.GetData(), src.types.GetData(), types.GetCount());
+
+		size_t start = 0, end = 0;
+
+		for(size_t i = 0, j = src.types.GetCount(); i <= j; i++)
+		{
+			if(i > 0 && (i == j || (src.types[i] & ~PT_CLOSEFIGURE) == PT_MOVETO))
+			{
+				end = i-1;
+
+				bool cw = true; // TODO: determine orientation (ttf is always cw and we are sill before Transform)
+				float rotate = cw ? -M_PI_2 : M_PI_2;
+
+				CPoint prev = src.points[end];
+				CPoint cur = src.points[start];
+
+				for(size_t k = start; k <= end; k++)
+				{
+					CPoint next = k < end ? src.points[k+1] : src.points[start];
+
+					for(int l = k-1; prev == cur; l--)
+					{
+						if(l < (int)start) l = end;
+						prev = src.points[l];
+					}
+
+					for(int l = k+1; next == cur; l++)
+					{
+						if(l > (int)end) l = start;
+						next = src.points[l];
+					}
+
+					CPoint in = cur - prev;
+					CPoint out = next - cur;
+
+					float angle_in = atan2((float)in.y, (float)in.x);
+					float angle_out = atan2((float)out.y, (float)out.x);
+					float angle_diff = angle_out - angle_in;
+					if(angle_diff < 0) angle_diff += M_PI*2;
+					if(angle_diff > M_PI) angle_diff -= M_PI*2;
+					float scale = cos(angle_diff / 2);
+
+					CPoint p;
+
+					if(angle_diff < 0)
+					{
+						if(angle_diff > -M_PI/8) {if(scale < 1) scale = 1;}
+						else {if(scale < 0.50) scale = 0.50;}
+					}
+					else
+					{
+						if(angle_diff < M_PI/8) {if(scale < 0.75) scale = 0.75;}
+						else {if(scale < 0.25) scale = 0.25;}
+					}
+
+					if(scale < 0.1) scale = 0.1;
+
+					float angle = angle_in + angle_diff / 2 - rotate;
+					float radius = -size / scale; // FIXME
+
+					p.x = (int)(radius * cos(angle) + 0.5);
+					p.y = (int)(radius * sin(angle) + 0.5);
+
+					points[k] = cur + p;
+
+					prev = cur;
+					cur = next;
+				}
+
+				start = end+1;
+			}
+		}
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/GlyphPath.h b/vsfilter/libssf/GlyphPath.h
new file mode 100644
index 0000000000000000000000000000000000000000..4c5942e798f8adc75ab1a2c68177093850cd3f10
--- /dev/null
+++ b/vsfilter/libssf/GlyphPath.h
@@ -0,0 +1,45 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+
+namespace ssf
+{
+	class GlyphPath
+	{
+	public:
+		GlyphPath() {}
+		virtual ~GlyphPath() {}
+
+		GlyphPath(const GlyphPath& path);
+		void operator = (const GlyphPath& path);
+
+		bool IsEmpty();
+		void RemoveAll();
+		void MovePoints(const CPoint& o); 
+		void Enlarge(const GlyphPath& src, float size);
+
+		CAtlArray<BYTE> types;
+		CAtlArray<POINT> points;
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Node.cpp b/vsfilter/libssf/Node.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..798b435272b58bbe4bdff96c95280ac1a0cc58e1
--- /dev/null
+++ b/vsfilter/libssf/Node.cpp
@@ -0,0 +1,551 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Node.h"
+#include "NodeFactory.h"
+#include "Exception.h"
+#include "Split.h"
+
+#include <math.h>
+
+namespace ssf
+{
+	Node::Node(NodeFactory* pnf, CStringW name)
+		: m_pnf(pnf)
+		, m_type('?')
+		, m_name(name)
+		, m_priority(PNormal)
+		, m_predefined(false)
+		, m_parent(NULL)
+	{
+		ASSERT(m_pnf);
+	}
+
+	void Node::AddTail(Node* pNode)
+	{
+		if(POSITION pos = m_nodes.Find(pNode)) // TODO: slow
+		{
+			m_nodes.MoveToTail(pos);
+			return;
+		}
+
+		m_nodes.AddTail(pNode);
+		m_name2node[pNode->m_name] = pNode;
+	}
+
+	bool Node::IsNameUnknown()
+	{
+		return m_name.IsEmpty() || !!iswdigit(m_name[0]);
+	}
+
+	bool Node::IsTypeUnknown()
+	{
+		return m_type.IsEmpty() || m_type == '?';
+	}
+
+	bool Node::IsType(CStringW type)
+	{
+		return m_type == type;
+	}
+
+	void Node::GetChildDefs(CAtlList<Definition*>& l, LPCWSTR type, bool fFirst)
+	{
+		CAtlList<Definition*> rdl[3];
+
+		if(fFirst)
+		{
+			if(Definition* pDef = m_pnf->GetDefByName(m_type))
+			{
+				pDef->GetChildDefs(rdl[pDef->m_priority], type, false);
+			}
+		}
+
+		POSITION pos = m_nodes.GetHeadPosition();
+		while(pos)
+		{
+			if(Node* pNode = m_nodes.GetNext(pos))
+			{
+				pNode->GetChildDefs(rdl[pNode->m_priority], type, false);
+			}
+		}
+
+		for(int i = 0; i < sizeof(rdl)/sizeof(rdl[0]); i++)
+		{
+			l.AddTailList(&rdl[i]);
+		}
+	}
+
+	// Reference
+
+	Reference::Reference(NodeFactory* pnf, CStringW name)
+		: Node(pnf, name)
+	{
+	}
+
+	Reference::~Reference()
+	{
+	}
+
+	void Reference::GetChildDefs(CAtlList<Definition*>& l, LPCWSTR type, bool fFirst)
+	{
+		CAtlList<Definition*> rdl[3];
+
+		POSITION pos = m_nodes.GetHeadPosition();
+		while(pos)
+		{
+			if(Definition* pDef = dynamic_cast<Definition*>(m_nodes.GetNext(pos)))
+			{
+				if(!type || pDef->m_type == type) // TODO: faster lookup
+				{
+					rdl[pDef->m_priority].AddTail(pDef);
+				}
+			}
+		}
+
+		for(int i = 0; i < sizeof(rdl)/sizeof(rdl[0]); i++)
+		{
+			l.AddTailList(&rdl[i]);
+		}
+	}
+
+	void Reference::Dump(OutputStream& s, int level, bool fLast)
+	{
+		if(m_predefined) return;
+
+		CStringW tabs(' ', level*4);
+
+		// s.PutString(tabs + '\n' + tabs + L" {\n");
+		s.PutString(L" {\n");
+
+		POSITION pos = m_nodes.GetHeadPosition();
+		while(pos)
+		{
+			Node* pNode = m_nodes.GetNext(pos);
+
+			if(Definition* pDef = dynamic_cast<Definition*>(pNode))
+			{
+				pDef->Dump(s, level + 1, pos == NULL);
+			}
+		}
+
+		s.PutString(tabs + '}');
+	}
+
+	// Definition
+
+	Definition::Definition(NodeFactory* pnf, CStringW name)
+		: Node(pnf, name)
+		, m_status(node)
+		, m_autotype(false)
+	{
+	}
+
+	Definition::~Definition()
+	{
+		RemoveFromCache();
+	}
+
+	bool Definition::IsVisible(Definition* pDef)
+	{
+		Node* pNode = m_parent;
+
+		while(pNode)
+		{
+			if(pNode->m_name2node.Lookup(pDef->m_name))
+			{
+				return true;
+			}
+
+			pNode = pNode->m_parent;
+		}
+
+		return false;
+	}
+
+	void Definition::AddTail(Node* pNode)
+	{
+//		if(Reference* pRef = dynamic_cast<Reference*>(pNode))
+		{
+			ASSERT(m_status == node);
+
+			m_status = node;
+
+			if(IsTypeUnknown() && !pNode->IsTypeUnknown())
+			{
+				m_type = pNode->m_type; 
+				m_autotype = true;
+			}
+
+			RemoveFromCache(pNode->m_type);
+		}
+
+		__super::AddTail(pNode);
+	}
+
+	Definition& Definition::operator[] (LPCWSTR type) 
+	{
+		Definition* pRetDef = NULL;
+		if(m_type2def.Lookup(type, pRetDef))
+			return *pRetDef;
+
+		pRetDef = new Definition(m_pnf, L"");
+		pRetDef->m_priority = PLow;
+		pRetDef->m_type = type;
+		m_type2def[type] = pRetDef;
+
+		CAtlList<Definition*> l;
+		GetChildDefs(l, type);
+
+		while(!l.IsEmpty())
+		{
+			Definition* pDef = l.RemoveHead();
+
+			pRetDef->m_priority = pDef->m_priority;
+			pRetDef->m_parent = pDef->m_parent;
+
+			if(pDef->IsValue())
+			{
+				pRetDef->SetAsValue(pDef->m_status, pDef->m_value, pDef->m_unit);
+			}
+			else
+			{
+				pRetDef->m_status = node; 
+				pRetDef->m_nodes.AddTailList(&pDef->m_nodes);
+			}
+		}
+
+		return *pRetDef;
+	}
+
+	void Definition::RemoveFromCache(LPCWSTR type)
+	{
+		if(!type)
+		{
+			POSITION pos = m_type2def.GetStartPosition();
+			while(pos) delete m_type2def.GetNextValue(pos);
+		}
+		else if(StringMapW<Definition*>::CPair* p = m_type2def.Lookup(type))
+		{
+			delete p->m_value;
+			m_type2def.RemoveKey(type);
+		}
+	}
+
+	bool Definition::IsValue(status_t s)
+	{
+		return s ? m_status == s : m_status != node;
+	}
+
+	void Definition::SetAsValue(status_t s, CStringW v, CStringW u)
+	{
+		ASSERT(s != node);
+
+		m_nodes.RemoveAll();
+		m_name2node.RemoveAll();
+
+		m_status = s;
+
+		m_value = v;
+		m_unit = u;
+	}
+
+	void Definition::SetAsNumber(CStringW v, CStringW u)
+	{
+		SetAsValue(number, v, u);
+
+		Number<float> n;
+		GetAsNumber(n); // will throw an exception if not a number
+	}
+
+	template<class T> 
+	void Definition::GetAsNumber(Number<T>& n, StringMapW<T>* n2n)
+	{
+		CStringW str = m_value;
+		str.Replace(L" ", L"");
+
+		n.value = 0;
+		n.unit = m_unit;
+		n.sign = 0;
+
+		if(n2n)
+		{
+			if(m_status == node) throw Exception(_T("expected value type"));
+
+			if(StringMapW<T>::CPair* p = n2n->Lookup(str))
+			{
+				n.value = p->m_value;
+				return;
+			}
+		}
+
+		if(m_status != number) throw Exception(_T("expected number"));
+
+		n.sign = str.Find('+') == 0 ? 1 : str.Find('-') == 0 ? -1 : 0;
+		str.TrimLeft(L"+-");
+
+		if(str.Find(L"0x") == 0)
+		{
+			if(n.sign) throw Exception(_T("hex values must be unsigned"));
+
+			n.value = (T)wcstoul(str.Mid(2), NULL, 16);
+		}
+		else
+		{
+			CStringW num_string = m_value + m_unit;
+
+			if(m_num_string != num_string)
+			{
+				Split sa(':', str);
+				Split sa2('.', sa ? sa[sa-1] : L"");
+
+				if(sa == 0 || sa2 == 0 || sa2 > 2) throw Exception(_T("invalid number"));
+
+				float f = 0;
+				for(size_t i = 0; i < sa; i++) {f *= 60; f += wcstoul(sa[i], NULL, 10);}
+				if(sa2 > 1) f += (float)wcstoul(sa2[1], NULL, 10) / pow((float)10, sa2[1].GetLength());
+
+				if(n.unit == L"ms") {f /= 1000; n.unit = L"s";}
+				else if(n.unit == L"m") {f *= 60; n.unit = L"s";}
+				else if(n.unit == L"h") {f *= 3600; n.unit = L"s";}
+
+				m_num.value = f;
+				m_num.unit = n.unit;
+				m_num_string = num_string;
+
+				n.value = (T)f;
+			}
+			else
+			{
+				n.value = (T)m_num.value;
+				n.unit = m_num.unit;
+			}
+
+			if(n.sign) n.value *= n.sign;
+		}
+	}
+
+	void Definition::GetAsString(CStringW& str)
+	{
+		if(m_status == node) throw Exception(_T("expected value type"));
+
+		str = m_value; 
+	}
+
+	void Definition::GetAsNumber(Number<int>& n, StringMapW<int>* n2n) {return GetAsNumber<int>(n, n2n);}
+	void Definition::GetAsNumber(Number<DWORD>& n, StringMapW<DWORD>* n2n) {return GetAsNumber<DWORD>(n, n2n);}
+	void Definition::GetAsNumber(Number<float>& n, StringMapW<float>* n2n) {return GetAsNumber<float>(n, n2n);}
+
+	void Definition::GetAsBoolean(bool& b)
+	{
+		static StringMapW<bool> s2b;
+
+		if(s2b.IsEmpty())
+		{
+			s2b[L"true"] = true;
+			s2b[L"on"] = true;
+			s2b[L"yes"] = true;
+			s2b[L"1"] = true;
+			s2b[L"false"] = false;
+			s2b[L"off"] = false;
+			s2b[L"no"] = false;
+			s2b[L"0"] = false;
+		}
+
+		if(!s2b.Lookup(m_value, b)) // m_status != boolean && m_status != number || 
+		{
+			throw Exception(_T("expected boolean"));
+		}
+	}
+
+	bool Definition::GetAsTime(Time& t, StringMapW<float>& offset, StringMapW<float>* n2n, int default_id)
+	{
+		Definition& time = (*this)[L"time"];
+
+		CStringW id;
+		if(time[L"id"].IsValue()) id = time[L"id"];
+		else id.Format(L"%d", default_id);
+
+		float scale = time[L"scale"].IsValue() ? time[L"scale"] : 1.0f;
+
+		if(time[L"start"].IsValue() && time[L"stop"].IsValue())
+		{
+			time[L"start"].GetAsNumber(t.start, n2n);
+			time[L"stop"].GetAsNumber(t.stop, n2n);
+
+			if(t.start.unit.IsEmpty()) t.start.value *= scale;
+			if(t.stop.unit.IsEmpty()) t.stop.value *= scale;
+
+			float o = 0;
+			offset.Lookup(id, o);
+
+			if(t.start.sign != 0) t.start.value = o + t.start.value;
+			if(t.stop.sign != 0) t.stop.value = t.start.value + t.stop.value;
+
+			offset[id] = t.stop.value;
+
+			return true;
+		}
+
+		return false;
+	}
+
+	Definition::operator LPCWSTR()
+	{
+		CStringW str;
+		GetAsString(str);
+		return str;
+	}
+
+	Definition::operator float()
+	{
+		float d;
+		GetAsNumber(d);
+		return d;
+	}
+
+	Definition::operator bool()
+	{
+		bool b;
+		GetAsBoolean(b);
+		return b;
+	}
+
+	Definition* Definition::SetChildAsValue(CStringW path, status_t s, CStringW v, CStringW u)
+	{
+		Definition* pDef = this;
+
+		Split split('.', path);
+
+		for(size_t i = 0, j = split-1; i <= j; i++)
+		{
+			CStringW type = split[i];
+
+			if(pDef->m_nodes.IsEmpty() || !dynamic_cast<Reference*>(pDef->m_nodes.GetTail()))
+			{
+				EXECUTE_ASSERT(m_pnf->CreateRef(pDef) != NULL);
+			}
+
+			if(Reference* pRef = dynamic_cast<Reference*>(pDef->m_nodes.GetTail()))
+			{
+				pDef = NULL;
+
+				POSITION pos = pRef->m_nodes.GetTailPosition();
+				while(pos)
+				{
+					Definition* pChildDef = dynamic_cast<Definition*>(pRef->m_nodes.GetPrev(pos));
+
+					if(pChildDef->IsType(type))
+					{
+						if(pChildDef->IsNameUnknown()) pDef = pChildDef;
+						break;
+					}
+				}
+
+				if(!pDef)
+				{
+					pDef = m_pnf->CreateDef(pRef, type);
+				}
+
+				if(i == j)
+				{
+					pDef->SetAsValue(s, v, u);
+					return pDef;
+				}
+			}
+		}
+
+		return NULL;
+	}
+
+	Definition* Definition::SetChildAsNumber(CStringW path, CStringW v, CStringW u)
+	{
+		Definition* pDef = SetChildAsValue(path, number, v, u);
+
+		Number<float> n;
+		pDef->GetAsNumber(n); // will throw an exception if not a number
+
+		return pDef;
+	}
+
+	void Definition::Dump(OutputStream& s, int level, bool fLast)
+	{
+		if(m_predefined) return;
+
+		CStringW tabs(' ', level*4);
+
+		CStringW str = tabs;
+		if(m_predefined) str += '?';
+		if(m_priority == PLow) str += '*';
+		else if(m_priority == PHigh) str += '!';
+		if(!IsTypeUnknown() && !m_autotype) str += m_type;
+		if(!IsNameUnknown()) str += '#' + m_name;
+		str += ':';
+		s.PutString(L"%s", str);
+
+		if(!m_nodes.IsEmpty())
+		{
+			POSITION pos = m_nodes.GetHeadPosition();
+			while(pos)
+			{
+				Node* pNode = m_nodes.GetNext(pos);
+
+				if(Reference* pRef = dynamic_cast<Reference*>(pNode))
+				{
+					pRef->Dump(s, level, fLast);
+				}
+				else 
+				{
+					ASSERT(!pNode->IsNameUnknown());
+					s.PutString(L" %s", pNode->m_name);
+				}
+			}
+
+			s.PutString(L";\n");
+
+			if(!fLast && (!m_nodes.IsEmpty() || level == 0)) s.PutString(L"\n");
+		}
+		else if(m_status == string)
+		{
+			CStringW str = m_value;
+			str.Replace(L"\"", L"\\\"");
+			s.PutString(L" \"%s\";\n", str);
+		}
+		else if(m_status == number)
+		{
+			CStringW str = m_value;
+			if(!m_unit.IsEmpty()) str += m_unit;
+			s.PutString(L" %s;\n", str);
+		}
+		else if(m_status == boolean)
+		{
+			s.PutString(L" %s;\n", m_value);
+		}
+		else if(m_status == block)
+		{
+			s.PutString(L" {%s};\n", m_value);
+		}
+		else
+		{
+			s.PutString(L" null;\n");
+		}
+	}
+}
diff --git a/vsfilter/libssf/Node.h b/vsfilter/libssf/Node.h
new file mode 100644
index 0000000000000000000000000000000000000000..f14168433f41f68cd14c16c8bb8a6f3cb4abcc69
--- /dev/null
+++ b/vsfilter/libssf/Node.h
@@ -0,0 +1,122 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "Stream.h"
+#include "StringMap.h"
+
+namespace ssf
+{
+	class Definition;
+	class NodeFactory;
+
+	enum NodePriority {PLow, PNormal, PHigh};
+
+	class Node
+	{
+	protected:
+		NodeFactory* m_pnf;
+
+	public:
+		Node* m_parent;
+		CAtlList<Node*> m_nodes;
+		StringMapW<Node*> m_name2node;
+		CStringW m_type, m_name;
+		NodePriority m_priority;
+		bool m_predefined;
+
+		Node(NodeFactory* pnf, CStringW name);
+		virtual ~Node() {}
+
+		bool IsNameUnknown();
+		bool IsTypeUnknown();
+		bool IsType(CStringW type);
+
+		virtual void AddTail(Node* pNode);
+		virtual void GetChildDefs(CAtlList<Definition*>& l, LPCWSTR type = NULL, bool fFirst = true);
+		virtual void Dump(OutputStream& s, int level = 0, bool fLast = false) = 0;
+	};
+
+	class Reference : public Node
+	{
+	public:
+		Reference(NodeFactory* pnf, CStringW name);
+		virtual ~Reference();
+
+		void GetChildDefs(CAtlList<Definition*>& l, LPCWSTR type = NULL, bool fFirst = true);
+		void Dump(OutputStream& s, int level = 0, bool fLast = false);
+	};
+
+	class Definition : public Node
+	{
+	public:
+		template<typename T> struct Number {T value; int sign; CStringW unit;};
+		struct Time {Number<float> start, stop;};
+
+		enum status_t {node, string, number, boolean, block};
+
+	private:
+		status_t m_status;
+		bool m_autotype;
+		CStringW m_value, m_unit;
+		Number<float> m_num;
+		CStringW m_num_string;
+
+		StringMapW<Definition*> m_type2def;
+		void RemoveFromCache(LPCWSTR type = NULL);
+
+		template<typename T> 
+		void GetAsNumber(Number<T>& n, StringMapW<T>* n2n = NULL);
+
+	public:
+		Definition(NodeFactory* pnf, CStringW name);
+		virtual ~Definition();
+
+		bool IsVisible(Definition* pDef);
+
+		void AddTail(Node* pNode);
+		void Dump(OutputStream& s, int level = 0, bool fLast = false);
+
+		Definition& operator[] (LPCWSTR type);
+
+		bool IsValue(status_t s = (status_t)0);
+
+		void SetAsValue(status_t s, CStringW v, CStringW u = L"");
+		void SetAsNumber(CStringW v, CStringW u = L"");
+
+		void GetAsString(CStringW& str);
+		void GetAsNumber(Number<int>& n, StringMapW<int>* n2n = NULL);
+		void GetAsNumber(Number<DWORD>& n, StringMapW<DWORD>* n2n = NULL);
+		void GetAsNumber(Number<float>& n, StringMapW<float>* n2n = NULL);
+		template<typename T> 
+		void GetAsNumber(T& t, StringMapW<T>* n2n = NULL) {Number<T> n; GetAsNumber(n, n2n); t = n.value;}
+		void GetAsBoolean(bool& b);
+		bool GetAsTime(Time& t, StringMapW<float>& offset, StringMapW<float>* n2n = NULL, int default_id = 0);
+
+		operator LPCWSTR();
+		operator float();
+		operator bool();
+
+		Definition* SetChildAsValue(CStringW path, status_t s, CStringW v, CStringW u = L"");
+		Definition* SetChildAsNumber(CStringW path, CStringW v, CStringW u = L"");
+	};
+}
diff --git a/vsfilter/libssf/NodeFactory.cpp b/vsfilter/libssf/NodeFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ded35838a796a7da9ebf9016dc54e244f35aad20
--- /dev/null
+++ b/vsfilter/libssf/NodeFactory.cpp
@@ -0,0 +1,182 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "NodeFactory.h"
+#include "Exception.h"
+
+namespace ssf
+{
+	NodeFactory::NodeFactory()
+		: m_counter(0)
+		, m_root(NULL)
+		, m_predefined(false)
+	{
+	}
+
+	NodeFactory::~NodeFactory()
+	{
+		RemoveAll();
+	}
+
+	CStringW NodeFactory::GenName()
+	{
+		CStringW name;
+		name.Format(L"%I64d", m_counter++);
+		return name;
+	}
+
+	void NodeFactory::RemoveAll()
+	{
+		m_root = NULL;
+
+		POSITION pos = m_nodes.GetStartPosition();
+		while(pos) delete m_nodes.GetNextValue(pos);
+		m_nodes.RemoveAll();
+
+		m_newnodes.RemoveAll();
+	}
+
+	void NodeFactory::Commit()
+	{
+		m_newnodes.RemoveAll();
+	}
+
+	void NodeFactory::Rollback()
+	{
+		POSITION pos = m_newnodes.GetTailPosition();
+		while(pos)
+		{
+			if(StringMap<Node*, CStringW>::CPair* p = m_nodes.Lookup(m_newnodes.GetPrev(pos)))
+			{
+				delete p->m_value; // TODO: remove it from "parent"->m_nodes too
+				m_nodes.RemoveKey(p->m_key);
+			}
+		}
+	}
+
+	Reference* NodeFactory::CreateRootRef()
+	{
+		RemoveAll();
+		m_root = CreateRef(NULL);
+		return m_root;
+	}
+
+	Reference* NodeFactory::GetRootRef() const
+	{
+		ASSERT(m_root);
+		return m_root;
+	}
+
+	Reference* NodeFactory::CreateRef(Definition* pParentDef)
+	{
+		CStringW name = GenName();
+
+		Reference* pRef = new Reference(this, name);
+
+		m_nodes.SetAt(name, pRef);
+		m_newnodes.AddTail(name);
+
+		if(pParentDef)
+		{
+			pParentDef->AddTail(pRef);
+			pRef->m_parent = pParentDef;
+		}
+
+		return pRef;
+	}
+
+	Definition* NodeFactory::CreateDef(Reference* pParentRef, CStringW type, CStringW name, NodePriority priority)
+	{
+		Definition* pDef = NULL;
+
+		if(name.IsEmpty())
+		{
+			name = GenName();
+		}
+		else 
+		{
+			pDef = GetDefByName(name);
+
+			if(pDef)
+			{
+				if(!pDef->m_predefined)
+				{
+					throw Exception(_T("redefinition of '%s' is not allowed"), CString(name));
+				}
+
+				if(!pDef->IsTypeUnknown() && !pDef->IsType(type))
+				{
+					throw Exception(_T("cannot redefine type of %s to %s"), CString(name), CString(type));
+				}
+			}
+		}
+
+		if(!pDef)
+		{
+			pDef = new Definition(this, name);
+
+			m_nodes.SetAt(name, pDef);
+			m_newnodes.AddTail(name);
+
+			if(pParentRef)
+			{
+				pParentRef->AddTail(pDef);
+				pDef->m_parent = pParentRef;
+			}
+		}
+
+		pDef->m_type = type;
+		pDef->m_priority = priority;
+		pDef->m_predefined = m_predefined;
+
+		return pDef;
+	}
+
+	Definition* NodeFactory::GetDefByName(CStringW name) const
+	{
+		Node* pNode = NULL;
+		m_nodes.Lookup(name, pNode);
+		return dynamic_cast<Definition*>(pNode);
+	}
+
+	void NodeFactory::GetNewDefs(CAtlList<Definition*>& defs)
+	{
+		defs.RemoveAll();
+
+		POSITION pos = m_newnodes.GetHeadPosition();
+		while(pos)
+		{
+			if(Definition* pDef = GetDefByName(m_newnodes.GetNext(pos)))
+			{
+				defs.AddTail(pDef);
+			}
+		}
+	}
+
+	void NodeFactory::Dump(OutputStream& s) const
+	{
+		if(!m_root) return;
+
+		POSITION pos = m_root->m_nodes.GetHeadPosition();
+		while(pos) m_root->m_nodes.GetNext(pos)->Dump(s);
+	}
+}
diff --git a/vsfilter/libssf/NodeFactory.h b/vsfilter/libssf/NodeFactory.h
new file mode 100644
index 0000000000000000000000000000000000000000..4bdca9d29a6a2367308f60e736281e80c244c758
--- /dev/null
+++ b/vsfilter/libssf/NodeFactory.h
@@ -0,0 +1,58 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "Node.h"
+
+namespace ssf
+{
+	class NodeFactory
+	{
+		Reference* m_root;
+		StringMapW<Node*> m_nodes;
+		CAtlList<CStringW> m_newnodes;
+		bool m_predefined;
+
+		unsigned __int64 m_counter;
+		CStringW GenName();
+
+	public:
+		NodeFactory();
+		virtual ~NodeFactory();
+
+		virtual void RemoveAll();
+
+		void SetPredefined(bool predefined) {m_predefined = predefined;}
+
+		void Commit();
+		void Rollback();
+
+		Reference* CreateRootRef();
+		Reference* GetRootRef() const;
+		Reference* CreateRef(Definition* pParentDef);
+		Definition* CreateDef(Reference* pParentRef = NULL, CStringW type = L"", CStringW name = L"", NodePriority priority = PNormal);
+		Definition* GetDefByName(CStringW name) const;
+		void GetNewDefs(CAtlList<Definition*>& defs);
+
+		void Dump(OutputStream& s) const;
+	};
+}
diff --git a/vsfilter/libssf/Rasterizer.cpp b/vsfilter/libssf/Rasterizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5db7a8e31fe5f52e56e5a456e0356e42fb65c062
--- /dev/null
+++ b/vsfilter/libssf/Rasterizer.cpp
@@ -0,0 +1,708 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ *  Based on the rasterizer of virtualdub's subtitler plugin
+ */
+
+#include "stdafx.h"
+#include <vector>
+#include <algorithm>
+#include "Rasterizer.h"
+#include "Glyph.h"
+
+#define FONT_AA 3
+#define FONT_SCALE (6-FONT_AA)
+
+// FONT_AA: 0 - 3
+
+
+namespace ssf
+{
+	template<class T> T mymax(T a, T b) {return a > b ? a : b;}
+	template<class T> T mymin(T a, T b) {return a < b ? a : b;}
+
+	Rasterizer::Rasterizer()
+	{
+		mpOverlayBuffer = NULL;
+		mOverlayWidth = mOverlayHeight = 0;
+		mPathOffsetX = mPathOffsetY = 0;
+		mOffsetX = mOffsetY = 0;
+	}
+
+	Rasterizer::~Rasterizer()
+	{
+		_TrashOverlay();
+	}
+
+	void Rasterizer::_TrashOverlay()
+	{
+		if(mpOverlayBuffer) delete [] mpOverlayBuffer;
+		mpOverlayBuffer = NULL;
+	}
+
+	void Rasterizer::_ReallocEdgeBuffer(int edges)
+	{
+		mEdgeHeapSize = edges;
+		mpEdgeBuffer = (Edge*)realloc(mpEdgeBuffer, sizeof(Edge)*edges);
+	}
+
+	void Rasterizer::_EvaluateBezier(const CPoint& p0, const CPoint& p1, const CPoint& p2, const CPoint& p3)
+	{
+		if(abs(p0.x + p2.x - p1.x*2) +
+		   abs(p0.y + p2.y - p1.y*2) +
+		   abs(p1.x + p3.x - p2.x*2) +
+		   abs(p1.y + p3.y - p2.y*2) <= max(2, 1<<FONT_AA))
+		{
+			_EvaluateLine(p0, p3);
+		}
+		else
+		{
+			CPoint p01, p12, p23, p012, p123, p0123;
+
+			p01.x = (p0.x + p1.x + 1) >> 1;
+			p01.y = (p0.y + p1.y + 1) >> 1;
+			p12.x = (p1.x + p2.x + 1) >> 1;
+			p12.y = (p1.y + p2.y + 1) >> 1;
+			p23.x = (p2.x + p3.x + 1) >> 1;
+			p23.y = (p2.y + p3.y + 1) >> 1;
+			p012.x = (p01.x + p12.x + 1) >> 1;
+			p012.y = (p01.y + p12.y + 1) >> 1;
+			p123.x = (p12.x + p23.x + 1) >> 1;
+			p123.y = (p12.y + p23.y + 1) >> 1;
+			p0123.x = (p012.x + p123.x + 1) >> 1;
+			p0123.y = (p012.y + p123.y + 1) >> 1;
+
+			_EvaluateBezier(p0, p01, p012, p0123);
+			_EvaluateBezier(p0123, p123, p23, p3);
+		}
+	}
+
+	void Rasterizer::_EvaluateLine(CPoint p0, CPoint p1)
+	{
+		if(lastp != p0)
+		{
+			_EvaluateLine(lastp, p0);
+		}
+
+		if(!fFirstSet)
+		{
+			firstp = p0; 
+			fFirstSet = true;
+		}
+
+		lastp = p1;
+
+		// TODO: ((1<<FONT_SCALE)/2+-1)  
+
+		if(p1.y > p0.y)	// down
+		{
+			int xacc = p0.x << (8 - FONT_SCALE);
+
+			// prestep p0.y down
+
+			int dy = p1.y - p0.y;
+			int y = ((p0.y + ((1<<FONT_SCALE)/2-1)) & ~((1<<FONT_SCALE)-1)) + (1<<FONT_SCALE)/2;
+			int iy = y >> FONT_SCALE;
+
+			p1.y = (p1.y - ((1<<FONT_SCALE)/2+1)) >> FONT_SCALE;
+
+			if(iy <= p1.y)
+			{
+				int invslope = ((p1.x - p0.x) << 8) / dy;
+
+				while(mEdgeNext + p1.y + 1 - iy > mEdgeHeapSize)
+					_ReallocEdgeBuffer(mEdgeHeapSize*2);
+
+				xacc += (invslope * (y - p0.y)) >> FONT_SCALE;
+
+				while(iy <= p1.y)
+				{
+					int ix = (xacc + 128) >> 8;
+
+					mpEdgeBuffer[mEdgeNext].next = mpScanBuffer[iy];
+					mpEdgeBuffer[mEdgeNext].posandflag = ix*2 + 1;
+
+					mpScanBuffer[iy] = mEdgeNext++;
+
+					++iy;
+					xacc += invslope;
+				}
+			}
+		}
+		else if(p1.y < p0.y) // up
+		{
+			int xacc = p1.x << (8 - FONT_SCALE);
+
+			// prestep p1.y down
+
+			int dy = p0.y - p1.y;
+			int y = ((p1.y + ((1<<FONT_SCALE)/2-1)) & ~((1<<FONT_SCALE)-1)) + (1<<FONT_SCALE)/2;
+			int iy = y >> FONT_SCALE;
+
+			p0.y = (p0.y - ((1<<FONT_SCALE)/2+1)) >> FONT_SCALE;
+
+			if(iy <= p0.y)
+			{
+				int invslope = ((p0.x - p1.x) << 8) / dy;
+
+				while(mEdgeNext + p0.y + 1 - iy > mEdgeHeapSize)
+					_ReallocEdgeBuffer(mEdgeHeapSize*2);
+
+				xacc += (invslope * (y - p1.y)) >> FONT_SCALE;
+
+				while(iy <= p0.y)
+				{
+					int ix = (xacc + 128) >> 8;
+
+					mpEdgeBuffer[mEdgeNext].next = mpScanBuffer[iy];
+					mpEdgeBuffer[mEdgeNext].posandflag = ix*2;
+
+					mpScanBuffer[iy] = mEdgeNext++;
+
+					++iy;
+					xacc += invslope;
+				}
+			}
+		}
+	}
+
+	bool Rasterizer::ScanConvert(GlyphPath& path, const CRect& bbox)
+	{
+		// Drop any outlines we may have.
+
+		mOutline.RemoveAll();
+		mWideOutline.RemoveAll();
+
+		if(path.types.IsEmpty() || path.points.IsEmpty() || bbox.IsRectEmpty())
+		{
+			mPathOffsetX = mPathOffsetY = 0;
+			mWidth = mHeight = 0;
+			return 0;
+		}
+
+		int minx = (bbox.left >> FONT_SCALE) & ~((1<<FONT_SCALE)-1);
+		int miny = (bbox.top >> FONT_SCALE) & ~((1<<FONT_SCALE)-1);
+		int maxx = (bbox.right + ((1<<FONT_SCALE)-1)) >> FONT_SCALE;
+		int maxy = (bbox.bottom + ((1<<FONT_SCALE)-1)) >> FONT_SCALE;
+
+		path.MovePoints(CPoint(-minx*(1<<FONT_SCALE), -miny*(1<<FONT_SCALE)));
+
+		if(minx > maxx || miny > maxy)
+		{
+			mWidth = mHeight = 0;
+			mPathOffsetX = mPathOffsetY = 0;
+			return true;
+		}
+
+		mWidth = maxx + 1 - minx;
+		mHeight = maxy + 1 - miny;
+
+		mPathOffsetX = minx;
+		mPathOffsetY = miny;
+
+		// Initialize edge buffer.  We use edge 0 as a sentinel.
+
+		mEdgeNext = 1;
+		mEdgeHeapSize = 0x10000;
+		mpEdgeBuffer = (Edge*)malloc(sizeof(Edge)*mEdgeHeapSize);
+
+		// Initialize scanline list.
+
+		mpScanBuffer = new unsigned int[mHeight];
+		memset(mpScanBuffer, 0, mHeight*sizeof(unsigned int));
+
+		// Scan convert the outline.  Yuck, Bezier curves....
+
+		// Unfortunately, Windows 95/98 GDI has a bad habit of giving us text
+		// paths with all but the first figure left open, so we can't rely
+		// on the PT_CLOSEFIGURE flag being used appropriately.
+
+		fFirstSet = false;
+		firstp.x = firstp.y = 0;
+		lastp.x = lastp.y = 0;
+
+		int lastmoveto = -1;
+
+		BYTE* type = path.types.GetData();
+		POINT* pt = path.points.GetData();
+
+		for(size_t i = 0, j = path.types.GetCount(); i < j; i++)
+		{
+			switch(type[i] & ~PT_CLOSEFIGURE)
+			{
+			case PT_MOVETO:
+				if(lastmoveto >= 0 && firstp != lastp) _EvaluateLine(lastp, firstp);
+				lastmoveto = i;
+				fFirstSet = false;
+				lastp = pt[i];
+				break;
+			case PT_LINETO:
+				if(j - (i-1) >= 2) _EvaluateLine(pt[i-1], pt[i]);
+				break;
+			case PT_BEZIERTO:
+				if(j - (i-1) >= 4) _EvaluateBezier(pt[i-1], pt[i], pt[i+1], pt[i+2]);
+				i += 2;
+				break;
+			}
+		}
+
+		if(lastmoveto >= 0 && firstp != lastp) _EvaluateLine(lastp, firstp);
+
+		// Convert the edges to spans.  We couldn't do this before because some of
+		// the regions may have winding numbers >+1 and it would have been a pain
+		// to try to adjust the spans on the fly.  We use one heap to detangle
+		// a scanline's worth of edges from the singly-linked lists, and another
+		// to collect the actual scans.
+
+		std::vector<int> heap;
+
+		mOutline.SetCount(0, mEdgeNext / 2);
+
+		for(int y = 0; y < mHeight; y++)
+		{
+			int count = 0;
+
+			// Detangle scanline into edge heap.
+
+			for(unsigned int ptr = mpScanBuffer[y]; ptr; ptr = mpEdgeBuffer[ptr].next)
+			{
+				heap.push_back(mpEdgeBuffer[ptr].posandflag);
+			}
+
+			// Sort edge heap.  Note that we conveniently made the opening edges
+			// one more than closing edges at the same spot, so we won't have any
+			// problems with abutting spans.
+
+			std::sort(heap.begin(), heap.end());
+
+			// Process edges and add spans.  Since we only check for a non-zero
+			// winding number, it doesn't matter which way the outlines go!
+
+			std::vector<int>::iterator itX1 = heap.begin();
+			std::vector<int>::iterator itX2 = heap.end();
+
+			int x1, x2;
+
+			for(; itX1 != itX2; ++itX1)
+			{
+				int x = *itX1;
+
+				if(!count) 
+				{
+					x1 = x >> 1;
+				}
+
+				if(x&1) ++count;
+				else --count;
+
+				if(!count)
+				{
+					x2 = x >> 1;
+
+					if(x2 > x1)
+					{
+						Span s(x1, y, x2, y);
+						s.first += 0x4000000040000000i64;
+						s.second += 0x4000000040000000i64;
+						mOutline.Add(s);
+					}
+				}
+			}
+
+			heap.clear();
+		}
+
+		// Dump the edge and scan buffers, since we no longer need them.
+
+		free(mpEdgeBuffer);
+		delete [] mpScanBuffer;
+
+		// All done!
+
+		return true;
+	}
+
+	void Rasterizer::_OverlapRegion(Array<Span>& dst, Array<Span>& src, int dx, int dy)
+	{
+		mWideOutlineTmp.Move(dst);
+
+		Span* a = mWideOutlineTmp.GetData();
+		Span* ae = a + mWideOutlineTmp.GetCount();
+		Span* b = src.GetData();
+		Span* be = b + src.GetCount();
+
+		Span o(0, dy, 0, dy);
+		o.first -= dx;
+		o.second += dx;
+
+		while(a != ae && b != be)
+		{
+			Span x;
+
+			if(b->first + o.first < a->first)
+			{
+				// B span is earlier.  Use it.
+
+				x.first = b->first + o.first;
+				x.second = b->second + o.second;
+
+				b++;
+
+				// B spans don't overlap, so begin merge loop with A first.
+
+				for(;;)
+				{
+					// If we run out of A spans or the A span doesn't overlap,
+					// then the next B span can't either (because B spans don't
+					// overlap) and we exit.
+
+					if(a == ae || a->first > x.second)
+						break;
+
+					do {x.second = mymax(x.second, a->second);}
+					while(++a != ae && a->first <= x.second);
+
+					// If we run out of B spans or the B span doesn't overlap,
+					// then the next A span can't either (because A spans don't
+					// overlap) and we exit.
+
+					if(b == be || b->first + o.first > x.second)
+						break;
+
+					do {x.second = mymax(x.second, b->second + o.second);}
+					while(++b != be && b->first + o.first <= x.second);
+				}
+			}
+			else
+			{
+				// A span is earlier.  Use it.
+
+				x = *a;
+
+				a++;
+
+				// A spans don't overlap, so begin merge loop with B first.
+
+				for(;;)
+				{
+					// If we run out of B spans or the B span doesn't overlap,
+					// then the next A span can't either (because A spans don't
+					// overlap) and we exit.
+
+					if(b == be || b->first + o.first > x.second)
+						break;
+
+					do {x.second = mymax(x.second, b->second + o.second);}
+					while(++b != be && b->first + o.first <= x.second);
+
+					// If we run out of A spans or the A span doesn't overlap,
+					// then the next B span can't either (because B spans don't
+					// overlap) and we exit.
+
+					if(a == ae || a->first > x.second)
+						break;
+
+					do {x.second = mymax(x.second, a->second);}
+					while(++a != ae && a->first <= x.second);
+				}
+			}
+
+			// Flush span.
+
+			dst.Add(x);
+		}
+
+		// Copy over leftover spans.
+
+		dst.Append(a, ae - a);
+
+		for(; b != be; b++)
+		{
+			dst.Add(Span(b->first + o.first, b->second + o.second));
+		}
+	}
+
+	bool Rasterizer::CreateWidenedRegion(int r)
+	{
+		if(r < 0) r = 0;
+
+		r >>= FONT_SCALE;
+
+		for(int y = -r; y <= r; ++y)
+		{
+			int x = (int)(0.5f + sqrt(float(r*r - y*y)));
+
+			_OverlapRegion(mWideOutline, mOutline, x, y);
+		}
+
+		mWideBorder = r;
+
+		return true;
+	}
+
+	bool Rasterizer::Rasterize(int xsub, int ysub)
+	{
+		_TrashOverlay();
+
+		if(!mWidth || !mHeight)
+		{
+			mOverlayWidth = mOverlayHeight = 0;
+			return true;
+		}
+
+		xsub >>= FONT_SCALE;
+		ysub >>= FONT_SCALE;
+
+		xsub &= (1<<FONT_AA)-1;
+		ysub &= (1<<FONT_AA)-1;
+
+		int width = mWidth + xsub;
+		int height = mHeight + ysub;
+
+		mOffsetX = mPathOffsetX - xsub;
+		mOffsetY = mPathOffsetY - ysub;
+
+		int border = ((mWideBorder + ((1<<FONT_AA)-1)) & ~((1<<FONT_AA)-1)) + (1<<FONT_AA)*4;
+
+		if(!mWideOutline.IsEmpty())
+		{
+			width += 2*border;
+			height += 2*border;
+
+			xsub += border;
+			ysub += border;
+
+			mOffsetX -= border;
+			mOffsetY -= border;
+		}
+
+		mOverlayWidth = ((width + ((1<<FONT_AA)-1)) >> FONT_AA) + 1;
+		mOverlayHeight = ((height + ((1<<FONT_AA)-1)) >> FONT_AA) + 1;
+
+		mpOverlayBuffer = new BYTE[4 * mOverlayWidth * mOverlayHeight];
+		memset(mpOverlayBuffer, 0, 4 * mOverlayWidth * mOverlayHeight);
+
+		Array<Span>* pOutline[2] = {&mOutline, &mWideOutline};
+
+		for(int i = 0; i < countof(pOutline); i++)
+		{
+			const Span* s = pOutline[i]->GetData();
+
+			for(size_t j = 0, k = pOutline[i]->GetCount(); j < k; j++)
+			{
+				int y = s[j].y1 - 0x40000000 + ysub;
+				int x1 = s[j].x1 - 0x40000000 + xsub;
+				int x2 = s[j].x2 - 0x40000000 + xsub;
+
+				if(x2 > x1)
+				{
+					int first = x1 >> FONT_AA;
+					int last = (x2-1) >> FONT_AA;
+
+					BYTE* dst = mpOverlayBuffer + 4*(mOverlayWidth * (y >> FONT_AA) + first) + i;
+
+					if(first == last)
+					{
+						*dst += x2 - x1;
+					}
+					else
+					{
+						*dst += (((first+1) << FONT_AA) - x1) << (6 - FONT_AA*2);
+						dst += 4;
+
+						while(++first < last)
+						{
+							*dst += (1 << FONT_AA) << (6 - FONT_AA*2);
+							dst += 4;
+						}
+
+						*dst += (x2 - (last << FONT_AA)) << (6 - FONT_AA*2);
+					}
+				}
+			}
+		}
+
+		if(!mWideOutline.IsEmpty())
+		{
+			BYTE* p = mpOverlayBuffer;
+
+			for(int j = 0; j < mOverlayHeight; j++, p += mOverlayWidth*4)
+			{
+				for(int i = 0; i < mOverlayWidth; i++)
+				{
+					p[i*4+2] = min(p[i*4+1], (1<<6) - p[i*4]); // TODO: sse2
+				}
+			}
+		}
+
+		return true;
+	}
+
+	void Rasterizer::Blur(float n, int plane)
+	{
+		if(n <= 0 || !mOverlayWidth || !mOverlayHeight || !mpOverlayBuffer)
+			return;
+
+		int w = mOverlayWidth;
+		int h = mOverlayHeight;
+		int pitch = w*4;
+		BYTE* q0 = new BYTE[w*h];
+
+		for(int pass = 0, limit = (int)(n + 0.5); pass < n; pass++)
+		{
+			BYTE* p = mpOverlayBuffer + plane;
+			BYTE* q = q0;
+
+			for(int y = 0; y < h; y++, p += pitch, q += w)
+			{
+				q[0] = (2*p[0] + p[4]) >> 2;
+				int x = 0;
+				for(x = 1; x < w-1; x++)
+					q[x] = (p[(x-1)*4] + 2*p[x*4] + p[(x+1)*4]) >> 2;
+				q[x] = (p[(x-1)*4] + 2*p[x*4]) >> 2;
+			}
+
+			p = mpOverlayBuffer + plane;
+			q = q0;
+
+			for(int x = 0; x < w; x++, p += 4, q++)
+			{
+				p[0] = (2*q[0] + q[w]) >> 2;
+				int y = 0, yp, yq;
+				for(y = 1, yp = y*pitch, yq = y*w; y < h-1; y++, yp += pitch, yq += w)
+					p[yp] = (q[yq-w] + 2*q[yq] + q[yq+w]) >> 2;
+				p[yp] = (q[yq-w] + 2*q[yq]) >> 2;
+			}
+		}
+
+		delete [] q0;
+	}
+
+	void Rasterizer::Reuse(Rasterizer& r)
+	{
+		mWidth = r.mWidth;
+		mHeight = r.mHeight;
+		mPathOffsetX = r.mPathOffsetX;
+		mPathOffsetY = r.mPathOffsetY;
+		mWideBorder = r.mWideBorder;
+		mOutline.Move(r.mOutline);
+		mWideOutline.Move(r.mWideOutline);
+	}
+
+	///////////////////////////////////////////////////////////////////////////
+
+	static __forceinline void pixmix_c(DWORD* dst, DWORD color, DWORD alpha)
+	{
+		int a = ((alpha * (color>>24)) >> 6) & 0xff;
+		int ia = 0xff - a;
+
+		*dst = ((((*dst & 0x00ff00ff)*ia + (color & 0x00ff00ff)*a) & 0xff00ff00) >> 8)
+			| ((((*dst>>8) & 0x00ff00ff)*ia + ((color>>8) & 0x000000ff)*a) & 0xff00ff00);
+	}
+
+	static __forceinline void pixmix_sse2(DWORD* dst, DWORD color, DWORD alpha)
+	{
+		alpha = ((alpha * (color>>24)) >> 6) & 0xff;
+		color &= 0xffffff;
+
+		__m128i zero = _mm_setzero_si128();
+		__m128i a = _mm_set1_epi32((alpha << 16) | (0xff - alpha));
+		__m128i d = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*dst), zero);
+		__m128i s = _mm_unpacklo_epi8(_mm_cvtsi32_si128(color), zero);
+		__m128i r = _mm_unpacklo_epi16(d, s);
+
+		r = _mm_madd_epi16(r, a);
+		r = _mm_srli_epi32(r, 8);
+		r = _mm_packs_epi32(r, r);
+		r = _mm_packus_epi16(r, r);
+
+		*dst = (DWORD)_mm_cvtsi128_si32(r);
+	}
+
+	CRect Rasterizer::Draw(const SubPicDesc& spd, const CRect& clip, int xsub, int ysub, const DWORD* switchpts, int plane)
+	{
+		CRect bbox(0, 0, 0, 0);
+
+		if(!switchpts) return bbox;
+
+		// clip
+
+		CRect r(0, 0, spd.w, spd.h);
+		r &= clip;
+
+		xsub >>= FONT_SCALE;
+		ysub >>= FONT_SCALE;
+
+		int x = (xsub + mOffsetX + (1<<FONT_AA)/2) >> FONT_AA;
+		int y = (ysub + mOffsetY + (1<<FONT_AA)/2) >> FONT_AA;
+		int w = mOverlayWidth;
+		int h = mOverlayHeight;
+		int xo = 0, yo = 0;
+
+		if(x < r.left) {xo = r.left - x; w -= r.left - x; x = r.left;}
+		if(y < r.top) {yo = r.top - y; h -= r.top - y; y = r.top;}
+		if(x+w > r.right) w = r.right - x;
+		if(y+h > r.bottom) h = r.bottom - y;
+
+		if(w <= 0 || h <= 0) return bbox;
+
+		bbox.SetRect(x, y, x + w, y + h);
+		bbox &= CRect(0, 0, spd.w, spd.h);
+
+		// draw
+
+		const BYTE* src = mpOverlayBuffer + 4*(mOverlayWidth * yo + xo) + plane;
+		DWORD* dst = (DWORD*)((BYTE*)spd.bits + spd.pitch * y) + x;
+
+		DWORD color = switchpts[0];
+
+		bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
+
+		while(h--)
+		{
+			if(switchpts[1] == 0xffffffff)
+			{
+				if(fSSE2) for(int wt=0; wt<w; ++wt) pixmix_sse2(&dst[wt], color, src[wt*4]);
+				else for(int wt=0; wt<w; ++wt) pixmix_c(&dst[wt], color, src[wt*4]);
+			}
+			else
+			{
+				const DWORD* sw = switchpts;
+
+				if(fSSE2) 
+				for(int wt=0; wt<w; ++wt)
+				{
+					if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];}
+					pixmix_sse2(&dst[wt], color, src[wt*4]);
+				}
+				else
+				for(int wt=0; wt<w; ++wt)
+				{
+					if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];}
+					pixmix_c(&dst[wt], color, src[wt*4]);
+				}
+			}
+
+			src += 4*mOverlayWidth;
+			dst = (DWORD*)((BYTE*)dst + spd.pitch);
+		}
+
+		return bbox;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Rasterizer.h b/vsfilter/libssf/Rasterizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..495cdd85471299d70416e82c352a8e49bcc17a8a
--- /dev/null
+++ b/vsfilter/libssf/Rasterizer.h
@@ -0,0 +1,82 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include "SubtitleFile.h"
+#include "Array.h"
+#include "GlyphPath.h"
+#include "../SubPic/ISubPic.h"
+
+namespace ssf
+{
+	class Rasterizer
+	{
+		bool fFirstSet;
+		CPoint firstp, lastp;
+
+	private:
+		int mWidth, mHeight;
+
+		union Span
+		{
+			struct {int x1, y1, x2, y2;};
+			struct {unsigned __int64 first, second;};
+			union Span() {}
+			union Span(int _x1, int _y1, int _x2, int _y2) {x1 = _x1; y1 = _y1; x2 = _x2; y2 = _y2;}
+			union Span(unsigned __int64 _first, unsigned __int64 _second) {first = _first; second = _second;}
+		};
+
+		Array<Span> mOutline, mWideOutline, mWideOutlineTmp;
+		int mWideBorder;
+
+		struct Edge {int next, posandflag;}* mpEdgeBuffer;
+		unsigned int mEdgeHeapSize;
+		unsigned int mEdgeNext;
+		unsigned int* mpScanBuffer;
+
+	protected:
+		BYTE* mpOverlayBuffer;
+		int mOverlayWidth, mOverlayHeight;
+		int mPathOffsetX, mPathOffsetY;
+		int mOffsetX, mOffsetY;
+
+	private:
+		void _TrashOverlay();
+		void _ReallocEdgeBuffer(int edges);
+		void _EvaluateBezier(const CPoint& p0, const CPoint& p1, const CPoint& p2, const CPoint& p3);
+		void _EvaluateLine(CPoint p0, CPoint p1);
+		void _OverlapRegion(Array<Span>& dst, Array<Span>& src, int dx, int dy);
+
+	public:
+		Rasterizer();
+		virtual ~Rasterizer();
+
+		bool ScanConvert(GlyphPath& path, const CRect& bbox);
+		bool CreateWidenedRegion(int r);
+		bool Rasterize(int xsub, int ysub);
+		void Reuse(Rasterizer& r);
+
+		void Blur(float n, int plane);
+		CRect Draw(const SubPicDesc& spd, const CRect& clip, int xsub, int ysub, const DWORD* switchpts, int plane);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Renderer.cpp b/vsfilter/libssf/Renderer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ca137407c1d985a49d60fe0c8da6a9174e49923
--- /dev/null
+++ b/vsfilter/libssf/Renderer.cpp
@@ -0,0 +1,819 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ *  TODO: do something about bidi (at least handle these ranges: 0590-07BF, FB1D-FDFF, FE70-FEFF)
+ *
+ */
+
+#include "stdafx.h"
+#include "Renderer.h"
+#include "Arabic.h"
+
+namespace ssf
+{
+	template <class T>
+	void ReverseList(T& l)
+	{
+		POSITION pos = l.GetHeadPosition();
+		while(pos)
+		{
+			POSITION cur = pos;
+			l.GetNext(pos);
+			l.AddHead(l.GetAt(cur));
+			l.RemoveAt(cur);
+		}
+	}
+
+	static CPoint GetAlignPoint(const Placement& placement, const Size& scale, const CRect& frame, const CSize& size)
+	{
+		CPoint p;
+
+		p.x = frame.left;
+		p.x += placement.pos.auto_x 
+			? placement.align.h * (frame.Width() - size.cx)
+			: placement.pos.x * scale.cx - placement.align.h * size.cx;
+
+		p.y = frame.top;
+		p.y += placement.pos.auto_y 
+			? placement.align.v * (frame.Height() - size.cy) 
+			: placement.pos.y * scale.cy - placement.align.v * size.cy;
+
+		return p;
+	}
+
+	static CPoint GetAlignPoint(const Placement& placement, const Size& scale, const CRect& frame)
+	{
+		CSize size(0, 0);
+		return GetAlignPoint(placement, scale, frame, size);
+	}
+
+	//
+
+	Renderer::Renderer()
+	{
+		m_hDC = CreateCompatibleDC(NULL);
+		SetBkMode(m_hDC, TRANSPARENT); 
+		SetTextColor(m_hDC, 0xffffff); 
+		SetMapMode(m_hDC, MM_TEXT);
+	}
+
+	Renderer::~Renderer()
+	{
+		DeleteDC(m_hDC);
+	}
+
+	void Renderer::NextSegment(const CAutoPtrList<Subtitle>& subs)
+	{
+		StringMapW<bool> names;
+		POSITION pos = subs.GetHeadPosition();
+		while(pos) names[subs.GetNext(pos)->m_name] = true;
+
+		pos = m_sra.GetStartPosition();
+		while(pos)
+		{
+			POSITION cur = pos;
+			const CStringW& name = m_sra.GetNextKey(pos);
+			if(!names.Lookup(name)) m_sra.RemoveAtPos(cur);
+		}
+	}
+
+	RenderedSubtitle* Renderer::Lookup(const Subtitle* s, const CSize& vs, const CRect& vr)
+	{
+		m_sra.UpdateTarget(vs, vr);
+
+		if(s->m_text.IsEmpty())
+			return NULL;
+
+		CRect spdrc = s->m_frame.reference == _T("video") ? vr : CRect(CPoint(0, 0), vs);
+
+		if(spdrc.IsRectEmpty())
+			return NULL;
+
+		RenderedSubtitle* rs = NULL;
+
+		if(m_rsc.Lookup(s->m_name, rs))
+		{
+			if(!s->m_animated && rs->m_spdrc == spdrc)
+				return rs;
+
+			m_rsc.Invalidate(s->m_name);
+		}
+
+		const Style& style = s->m_text.GetHead().style;
+
+		Size scale;
+
+		scale.cx = (float)spdrc.Width() / s->m_frame.resolution.cx;
+		scale.cy = (float)spdrc.Height() / s->m_frame.resolution.cy;
+
+		CRect frame;
+
+		frame.left = (int)(64.0f * (spdrc.left + style.placement.margin.l * scale.cx) + 0.5);
+		frame.top = (int)(64.0f * (spdrc.top + style.placement.margin.t * scale.cy) + 0.5);
+		frame.right = (int)(64.0f * (spdrc.right - style.placement.margin.r * scale.cx) + 0.5);
+		frame.bottom = (int)(64.0f * (spdrc.bottom - style.placement.margin.b * scale.cy) + 0.5);
+
+		CRect clip;
+
+		if(style.placement.clip.l == -1) clip.left = 0;
+		else clip.left = (int)(spdrc.left + style.placement.clip.l * scale.cx);
+		if(style.placement.clip.t == -1) clip.top = 0;
+		else clip.top = (int)(spdrc.top + style.placement.clip.t * scale.cy); 
+		if(style.placement.clip.r == -1) clip.right = vs.cx;
+		else clip.right = (int)(spdrc.left + style.placement.clip.r * scale.cx);
+		if(style.placement.clip.b == -1) clip.bottom = vs.cy;
+		else clip.bottom = (int)(spdrc.top + style.placement.clip.b * scale.cy);
+
+		clip.left = max(clip.left, 0);
+		clip.top = max(clip.top, 0);
+		clip.right = min(clip.right, vs.cx);
+		clip.bottom = min(clip.bottom, vs.cy);
+
+		scale.cx *= 64;
+		scale.cy *= 64;
+
+		bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up");
+
+		// create glyph paths
+
+		WCHAR c_prev = 0, c_next;
+
+		CAutoPtrList<Glyph> glyphs;
+
+		POSITION pos = s->m_text.GetHeadPosition();
+		while(pos)
+		{
+			const Text& t = s->m_text.GetNext(pos);
+
+			LOGFONT lf;
+			memset(&lf, 0, sizeof(lf));
+			lf.lfCharSet = DEFAULT_CHARSET;
+			_tcscpy_s(lf.lfFaceName, CString(t.style.font.face));
+			lf.lfHeight = (LONG)(t.style.font.size * scale.cy + 0.5);
+			lf.lfWeight = (LONG)(t.style.font.weight + 0.5);
+			lf.lfItalic = !!t.style.font.italic;
+			lf.lfUnderline = !!t.style.font.underline;
+			lf.lfStrikeOut = !!t.style.font.strikethrough;
+			lf.lfOutPrecision = OUT_TT_PRECIS;
+			lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+			lf.lfQuality = ANTIALIASED_QUALITY;
+			lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
+
+			FontWrapper* font;
+
+			if(!(font = m_fc.Create(m_hDC, lf)))
+			{
+				_tcscpy_s(lf.lfFaceName, _T("Arial"));
+
+				if(!(font = m_fc.Create(m_hDC, lf)))
+				{
+					ASSERT(0);
+					continue;
+				}
+			}
+
+			HFONT hOldFont = SelectFont(m_hDC, *font);
+
+			const TEXTMETRIC& tm = font->GetTextMetric();
+
+			for(LPCWSTR c = t.str; *c; c++)
+			{
+				CAutoPtr<Glyph> g(new Glyph());
+
+				g->c = *c;
+				g->style = t.style;
+				g->scale = scale;
+				g->vertical = vertical;
+				g->font = font;
+
+				c_next = !c[1] && pos ? c_next = s->m_text.GetAt(pos).str[0] : c[1];
+				Arabic::Replace(g->c, c_prev, c_next);
+				c_prev = c[0];
+
+				CSize extent;
+				GetTextExtentPoint32W(m_hDC, &g->c, 1, &extent);
+				ASSERT(extent.cx >= 0 && extent.cy >= 0);
+
+				if(vertical) 
+				{
+					g->spacing = (int)(t.style.font.spacing * scale.cy + 0.5);
+					g->ascent = extent.cx / 2;
+					g->descent = extent.cx - g->ascent;
+					g->width = extent.cy;
+
+					// TESTME
+					if(g->c == Text::SP)
+					{
+						g->width /= 2;
+					}
+				}
+				else
+				{
+					g->spacing = (int)(t.style.font.spacing * scale.cx + 0.5);
+					g->ascent = tm.tmAscent;
+					g->descent = tm.tmDescent;
+					g->width = extent.cx;
+				}
+
+				if(g->c == Text::LSEP)
+				{
+					g->spacing = 0;
+					g->width = 0;
+					g->ascent /= 2;
+					g->descent /= 2;
+				}
+				else
+				{
+					GlyphPath* path = m_gpc.Create(m_hDC, font, g->c);
+					if(!path) {ASSERT(0); continue;}
+					g->path = *path;
+				}
+
+				glyphs.AddTail(g);
+			}
+
+			SelectFont(m_hDC, hOldFont);
+		}
+
+		// break glyphs into rows
+
+		CAutoPtrList<Row> rows;
+		CAutoPtr<Row> row;
+
+		pos = glyphs.GetHeadPosition();
+		while(pos)
+		{
+			CAutoPtr<Glyph> g = glyphs.GetNext(pos);
+			if(!row) row.Attach(new Row());
+			WCHAR c = g->c;
+			row->AddTail(g);
+			if(c == Text::LSEP || !pos) rows.AddTail(row);
+		}
+
+		// kerning
+
+		if(s->m_direction.primary == _T("right")) // || s->m_direction.primary == _T("left")
+		{
+			for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos))
+			{
+				Row* r = rows.GetAt(rpos);
+
+				POSITION gpos = r->GetHeadPosition();
+				while(gpos)
+				{
+					Glyph* g1 = r->GetNext(gpos);
+					if(!gpos) break;
+
+					Glyph* g2 = r->GetAt(gpos);
+					if(g1->font != g2->font || !g1->style.font.kerning || !g2->style.font.kerning)
+						continue;
+
+					if(int size = g1->font->GetKernAmount(g1->c, g2->c))
+					{
+						g2->path.MovePoints(CPoint(size, 0));
+						g2->width += size;
+					}
+				}
+			}				
+		}
+
+		// wrap rows
+
+		if(s->m_wrap == _T("normal") || s->m_wrap == _T("even"))
+		{
+			int maxwidth = abs((int)(vertical ? frame.Height() : frame.Width()));
+			int minwidth = 0;
+
+			for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos))
+			{
+				Row* r = rows.GetAt(rpos);
+				
+				POSITION brpos = NULL;
+
+				if(s->m_wrap == _T("even"))
+				{
+					int fullwidth = 0;
+
+					for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+					{
+						const Glyph* g = r->GetAt(gpos);
+
+						fullwidth += g->width + g->spacing;
+					}
+
+					fullwidth = abs(fullwidth);
+					
+					if(fullwidth > maxwidth)
+					{
+						maxwidth = fullwidth / ((fullwidth / maxwidth) + 1);
+						minwidth = maxwidth;
+					}
+				}
+
+				int width = 0;
+
+				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+				{
+					const Glyph* g = r->GetAt(gpos);
+
+					width += g->width + g->spacing;
+
+					if(brpos && abs(width) > maxwidth && g->c != Text::SP)
+					{
+						row.Attach(new Row());
+						POSITION next = brpos;
+						r->GetNext(next);
+						do {row->AddHead(r->GetPrev(brpos));} while(brpos);
+						rows.InsertBefore(rpos, row);
+						while(!r->IsEmpty() && r->GetHeadPosition() != next) r->RemoveHeadNoReturn();
+						g = r->GetAt(gpos = next);
+						width = g->width + g->spacing;
+					}
+
+					if(abs(width) >= minwidth)
+					{
+						if(g->style.linebreak == _T("char")
+						|| g->style.linebreak == _T("word") && g->c == Text::SP)
+						{
+							brpos = gpos;
+						}
+					}
+				}
+			}
+		}
+
+		// trim rows
+
+		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
+		{
+			Row* r = rows.GetAt(pos);
+
+			while(!r->IsEmpty() && r->GetHead()->c == Text::SP)
+				r->RemoveHead();
+
+			while(!r->IsEmpty() && r->GetTail()->c == Text::SP)
+				r->RemoveTail();
+		}
+
+		// calc fill width for each glyph
+
+		CAtlList<Glyph*> glypsh2fill;
+		int fill_id = 0;
+		int fill_width = 0;
+
+		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
+		{
+			Row* r = rows.GetAt(pos);
+
+			POSITION gpos = r->GetHeadPosition();
+			while(gpos)
+			{
+				Glyph* g = r->GetNext(gpos);
+
+				if(!glypsh2fill.IsEmpty() && fill_id && (g->style.fill.id != fill_id || !pos && !gpos))
+				{
+					int w = (int)(g->style.fill.width * fill_width + 0.5);
+
+					while(!glypsh2fill.IsEmpty())
+					{
+						Glyph* g = glypsh2fill.RemoveTail();
+						fill_width -= g->width;
+						g->fill = w - fill_width;
+					}
+
+					ASSERT(glypsh2fill.IsEmpty());
+					ASSERT(fill_width == 0);
+
+					glypsh2fill.RemoveAll();
+					fill_width = 0;
+				}
+
+				fill_id = g->style.fill.id;
+
+				if(g->style.fill.id)
+				{
+					glypsh2fill.AddTail(g);
+					fill_width += g->width;
+				}
+			}
+		}
+
+		// calc row sizes and total subtitle size
+
+		CSize size(0, 0);
+
+		if(s->m_direction.secondary == _T("left") || s->m_direction.secondary == _T("up"))
+			ReverseList(rows);
+
+		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
+		{
+			Row* r = rows.GetAt(pos);
+
+			if(s->m_direction.primary == _T("left") || s->m_direction.primary == _T("up"))
+				ReverseList(*r);
+
+			int w = 0, h = 0;
+
+			r->width = 0;
+
+			for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+			{
+				const Glyph* g = r->GetAt(gpos);
+
+				w += g->width;
+				if(gpos) w += g->spacing;
+				h = max(h, g->ascent + g->descent);
+
+				r->width += g->width;
+				if(gpos) r->width += g->spacing;
+				r->ascent = max(r->ascent, g->ascent);
+				r->descent = max(r->descent, g->descent);
+				r->border = max(r->border, g->GetBackgroundSize());
+			}
+
+			for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+			{
+				Glyph* g = r->GetAt(gpos);
+				g->row_ascent = r->ascent;
+				g->row_descent = r->descent;
+			}
+
+			if(vertical)
+			{
+				size.cx += h;
+				size.cy = max(size.cy, w);
+			}
+			else
+			{
+				size.cx = max(size.cx, w);
+				size.cy += h;
+			}
+		}
+
+		// align rows and calc glyph positions
+
+		rs = new RenderedSubtitle(spdrc, clip);
+
+		CPoint p = GetAlignPoint(style.placement, scale, frame, size);
+		CPoint org = GetAlignPoint(style.placement, scale, frame);
+
+		// collision detection
+
+		if(!s->m_animated)
+		{
+			int tlb = !rows.IsEmpty() ? rows.GetHead()->border : 0;
+			int brb = !rows.IsEmpty() ? rows.GetTail()->border : 0;
+
+			CRect r(p, size);
+			m_sra.GetRect(r, s, style.placement.align, tlb, brb);
+			org += r.TopLeft() - p;
+			p = r.TopLeft();
+		}
+
+		CRect subrect(p, size);
+
+		// continue positioning
+
+		for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos))
+		{
+			Row* r = rows.GetAt(pos);
+
+			CSize rsize;
+			rsize.cx = rsize.cy = r->width;
+
+			if(vertical)
+			{
+				p.y = GetAlignPoint(style.placement, scale, frame, rsize).y;
+
+				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+				{
+					CAutoPtr<Glyph> g = r->GetAt(gpos);
+					g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5) + r->ascent - g->ascent;
+					g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5);
+					p.y += g->width + g->spacing;
+					rs->m_glyphs.AddTail(g);
+				}
+
+				p.x += r->ascent + r->descent;
+			}
+			else
+			{
+				p.x = GetAlignPoint(style.placement, scale, frame, rsize).x;
+
+				for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos))
+				{
+					CAutoPtr<Glyph> g = r->GetAt(gpos);
+					g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5);
+					g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5) + r->ascent - g->ascent;
+					p.x += g->width + g->spacing;
+					rs->m_glyphs.AddTail(g);
+				}
+
+				p.y += r->ascent + r->descent;
+			}
+		}
+
+		// bkg, precalc style.placement.path, transform
+
+		pos = rs->m_glyphs.GetHeadPosition();
+		while(pos)
+		{
+			Glyph* g = rs->m_glyphs.GetNext(pos);
+			g->CreateBkg();
+			g->CreateSplineCoeffs(spdrc);
+			g->Transform(org, subrect);
+		}
+
+		// merge glyphs (TODO: merge 'fill' too)
+
+		Glyph* g0 = NULL;
+
+		pos = rs->m_glyphs.GetHeadPosition();
+		while(pos)
+		{
+			POSITION cur = pos;
+
+			Glyph* g = rs->m_glyphs.GetNext(pos);
+
+			CRect r = g->bbox + g->tl;
+
+			int size = (int)(g->GetBackgroundSize() + 0.5);
+			int depth = (int)(g->GetShadowDepth() + 0.5);
+
+			r.InflateRect(size, size);
+			r.InflateRect(depth, depth);
+
+			r.left >>= 6;
+			r.top >>= 6;
+			r.right = (r.right + 32) >> 6;
+			r.bottom = (r.bottom + 32) >> 6;
+
+			if((r & clip).IsRectEmpty()) // clip
+			{
+				rs->m_glyphs.RemoveAt(cur);
+			}
+			else if(g0 && g0->style.IsSimilar(g->style)) // append
+			{
+				CPoint o = g->tl - g0->tl;
+
+				g->path.MovePoints(o);
+
+				g0->path.types.Append(g->path.types);
+				g0->path.points.Append(g->path.points);
+
+				g->path_bkg.MovePoints(o);
+
+				g0->path_bkg.types.Append(g->path_bkg.types);
+				g0->path_bkg.points.Append(g->path_bkg.points);
+
+				g0->bbox |= g->bbox + o;
+
+				rs->m_glyphs.RemoveAt(cur);
+			}
+			else // leave alone
+			{
+				g0 = g;
+			}
+		}
+
+		// rasterize
+
+		pos = rs->m_glyphs.GetHeadPosition();
+		while(pos) rs->m_glyphs.GetNext(pos)->Rasterize();
+
+		// cache
+
+		m_rsc.Add(s->m_name, rs);
+
+		m_fc.Flush();
+
+		return rs;
+	}
+
+	//
+
+	CRect RenderedSubtitle::Draw(SubPicDesc& spd) const
+	{
+		CRect bbox;
+		bbox.SetRectEmpty();
+
+		// shadow
+
+		POSITION pos = m_glyphs.GetHeadPosition();
+		while(pos)
+		{
+			Glyph* g = m_glyphs.GetNext(pos);
+
+			if(g->style.shadow.depth <= 0) continue;
+
+			DWORD c = g->style.shadow.color;
+			DWORD sw[6] = {c, -1};
+
+			bool outline = g->style.background.type == L"outline" && g->style.background.size > 0;
+
+			bbox |= g->ras_shadow.Draw(spd, m_clip, g->tls.x, g->tls.y, sw, outline ? 1 : 0);
+		}
+
+		// background
+
+		pos = m_glyphs.GetHeadPosition();
+		while(pos)
+		{
+			Glyph* g = m_glyphs.GetNext(pos);
+
+			DWORD c = g->style.background.color;
+			DWORD sw[6] = {c, -1};
+
+			if(g->style.background.type == L"outline" && g->style.background.size > 0)
+			{
+				bbox |= g->ras.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, g->style.font.color.a < 255 ? 2 : 1);
+			}
+			else if(g->style.background.type == L"enlarge" && g->style.background.size > 0
+			|| g->style.background.type == L"box" && g->style.background.size >= 0)
+			{
+				bbox |= g->ras_bkg.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, 0);
+			}
+		}
+
+		// body
+
+		pos = m_glyphs.GetHeadPosition();
+		while(pos)
+		{
+			Glyph* g = m_glyphs.GetNext(pos);
+
+			DWORD c = g->style.font.color;
+			DWORD sw[6] = {c, -1}; // TODO: fill
+
+			bbox |= g->ras.Draw(spd, m_clip, g->tl.x, g->tl.y, sw, 0);
+		}
+
+		return bbox;
+	}
+
+	//
+
+	void SubRectAllocator::UpdateTarget(const CSize& s, const CRect& r)
+	{
+		if(vs != s || vr != r) RemoveAll();
+		vs = s;
+		vr = r;
+	}
+	
+	void SubRectAllocator::GetRect(CRect& rect, const Subtitle* s, const Align& align, int tlb, int brb)
+	{
+		SubRect sr(rect, s->m_layer);
+		sr.rect.InflateRect(tlb, tlb, brb, brb);
+
+		StringMapW<SubRect>::CPair* pPair = Lookup(s->m_name);
+
+		if(pPair && pPair->m_value.rect != sr.rect)
+		{
+			RemoveKey(s->m_name);
+			pPair = NULL;
+		}
+
+		if(!pPair)
+		{
+			bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up");
+
+			bool fOK = false;
+
+			while(!fOK)
+			{
+				fOK = true;
+
+				POSITION pos = GetStartPosition();
+				while(pos)
+				{
+					const SubRect& sr2 = GetNextValue(pos);
+
+					if(sr.layer == sr2.layer && !(sr.rect & sr2.rect).IsRectEmpty())
+					{
+						if(vertical)
+						{
+							if(align.h < 0.5)
+							{
+								sr.rect.right = sr2.rect.right + sr.rect.Width();
+								sr.rect.left = sr2.rect.right;
+							}
+							else
+							{
+								sr.rect.left = sr2.rect.left - sr.rect.Width();
+								sr.rect.right = sr2.rect.left;
+							}
+						}
+						else
+						{
+							if(align.v < 0.5)
+							{
+								sr.rect.bottom = sr2.rect.bottom + sr.rect.Height();
+								sr.rect.top = sr2.rect.bottom;
+							}
+							else
+							{
+								sr.rect.top = sr2.rect.top - sr.rect.Height();
+								sr.rect.bottom = sr2.rect.top;
+							}
+						}
+
+						fOK = false;
+					}
+				}
+			}
+
+			SetAt(s->m_name, sr);
+
+			rect = sr.rect;
+			rect.DeflateRect(tlb, tlb, brb, brb);
+		}
+	}
+
+	//
+
+	FontWrapper* FontCache::Create(HDC hDC, const LOGFONT& lf)
+	{
+		CStringW key;
+
+		key.Format(L"%s,%d,%d,%d", 
+			CStringW(lf.lfFaceName), lf.lfHeight, lf.lfWeight, 
+			((lf.lfItalic&1)<<2) | ((lf.lfUnderline&1)<<1) | ((lf.lfStrikeOut&1)<<0));
+
+		FontWrapper* pFW = NULL;
+
+		if(m_key2obj.Lookup(key, pFW))
+		{
+			return pFW;
+		}
+
+		HFONT hFont;
+
+		if(!(hFont = CreateFontIndirect(&lf)))
+		{
+			ASSERT(0);
+			return NULL;
+		}
+
+		pFW = new FontWrapper(hDC, hFont, key);
+
+		Add(key, pFW, false);
+
+		return pFW;
+	}
+
+	//
+
+	GlyphPath* GlyphPathCache::Create(HDC hDC, const FontWrapper* f, WCHAR c)
+	{
+		CStringW key = CStringW((LPCWSTR)*f) + c;
+
+		GlyphPath* path = NULL;
+
+		if(m_key2obj.Lookup(key, path))
+		{
+			return path;
+		}
+
+		BeginPath(hDC);
+		TextOutW(hDC, 0, 0, &c, 1);
+		CloseFigure(hDC);
+		if(!EndPath(hDC)) {AbortPath(hDC); ASSERT(0); return NULL;}
+
+		path = new GlyphPath();
+
+		int count = GetPath(hDC, NULL, NULL, 0);
+
+		if(count > 0)
+		{
+			path->points.SetCount(count);
+			path->types.SetCount(count);
+
+			if(count != GetPath(hDC, path->points.GetData(), path->types.GetData(), count))
+			{
+				ASSERT(0);
+				delete path;
+				return NULL;
+			}
+		}
+
+		Add(key, path);
+
+		return path;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Renderer.h b/vsfilter/libssf/Renderer.h
new file mode 100644
index 0000000000000000000000000000000000000000..5364ef4ac1dfc3fc28cd8a93d41c528cb1c0607c
--- /dev/null
+++ b/vsfilter/libssf/Renderer.h
@@ -0,0 +1,160 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "StringMap.h"
+#include "Glyph.h"
+
+namespace ssf
+{
+	template<class T>
+	class Cache
+	{
+	protected:
+		StringMapW<T> m_key2obj;
+		CAtlList<CStringW> m_objs;
+		size_t m_limit;
+
+	public:
+		Cache(size_t limit) {m_limit = max(1, limit);}
+		virtual ~Cache() {RemoveAll();}
+
+		void RemoveAll()
+		{
+			POSITION pos = m_key2obj.GetStartPosition();
+			while(pos) delete m_key2obj.GetNextValue(pos);
+			m_key2obj.RemoveAll();
+			m_objs.RemoveAll();
+		}
+
+		void Add(const CStringW& key, T& obj, bool fFlush = true)
+		{
+			if(StringMapW<T>::CPair* p = m_key2obj.Lookup(key)) delete p->m_value;
+			else m_objs.AddTail(key);
+
+			m_key2obj[key] = obj;
+
+			if(fFlush) Flush();
+		}
+
+		void Flush()
+		{
+			while(m_objs.GetCount() > m_limit)
+			{
+				CStringW key = m_objs.RemoveHead();
+				ASSERT(m_key2obj.Lookup(key));
+				delete m_key2obj[key];
+				m_key2obj.RemoveKey(key);
+			}
+		}
+
+		bool Lookup(const CStringW& key, T& val)
+		{
+			return m_key2obj.Lookup(key, val) && val;
+		}
+
+		void Invalidate(const CStringW& key)
+		{
+			T val;
+			if(m_key2obj.Lookup(key, val))
+			{
+				delete val;
+				m_key2obj[key] = NULL;
+			}
+		}
+	};
+
+	class FontCache : public Cache<FontWrapper*> 
+	{
+	public:
+		FontCache() : Cache(20) {}
+		FontWrapper* Create(HDC hDC, const LOGFONT& lf);
+	};
+
+	class GlyphPathCache : public Cache<GlyphPath*>
+	{
+	public:
+		GlyphPathCache() : Cache(100) {}
+		GlyphPath* Create(HDC hDC, const FontWrapper* f, WCHAR c);
+	};
+
+	class Row : public CAutoPtrList<Glyph>
+	{
+	public:
+		int ascent, descent, border, width;
+		Row() {ascent = descent = border = width = 0;}
+	};
+
+	class RenderedSubtitle
+	{
+	public:
+		CRect m_spdrc;
+		CRect m_clip;
+		CAutoPtrList<Glyph> m_glyphs;
+
+		RenderedSubtitle(const CRect& spdrc, const CRect& clip) : m_spdrc(spdrc), m_clip(clip) {}
+		virtual ~RenderedSubtitle() {}
+
+		CRect Draw(SubPicDesc& spd) const;
+	};
+
+	class RenderedSubtitleCache : public Cache<RenderedSubtitle*>
+	{
+	public:
+		RenderedSubtitleCache() : Cache(10) {}
+	};
+
+	class SubRect
+	{
+	public:
+		CRect rect;
+		float layer;
+		SubRect() {}
+		SubRect(const CRect& r, float l) : rect(r), layer(l) {}
+	};
+
+	class SubRectAllocator : public StringMapW<SubRect>
+	{
+		CSize vs;
+		CRect vr;
+	public:
+		void UpdateTarget(const CSize& vs, const CRect& vr);
+		void GetRect(CRect& rect, const Subtitle* s, const Align& align, int tlb, int brb);
+	};
+
+	class Renderer
+	{
+		HDC m_hDC;
+
+		FontCache m_fc;
+		GlyphPathCache m_gpc;
+		RenderedSubtitleCache m_rsc;
+		SubRectAllocator m_sra;
+
+	public:
+		Renderer();
+		virtual ~Renderer();
+
+		void NextSegment(const CAutoPtrList<Subtitle>& subs);
+		RenderedSubtitle* Lookup(const Subtitle* s, const CSize& vs, const CRect& vr);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Split.cpp b/vsfilter/libssf/Split.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c84672e03fa393046a0336e371829b8f1cccfbb
--- /dev/null
+++ b/vsfilter/libssf/Split.cpp
@@ -0,0 +1,74 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Split.h"
+#include "Exception.h"
+
+namespace ssf
+{
+	Split::Split(LPCWSTR sep, CStringW str, size_t limit, SplitType type)
+	{
+		DoSplit(sep, str, limit, type);
+	}
+
+	Split::Split(WCHAR sep, CStringW str, size_t limit, SplitType type)
+	{
+		DoSplit(CStringW(sep), str, limit, type);
+	}
+
+	void Split::DoSplit(LPCWSTR sep, CStringW str, size_t limit, SplitType type)
+	{
+		RemoveAll();
+
+		if(size_t seplen = wcslen(sep))
+		{
+			for(int i = 0, j = 0, len = str.GetLength(); 
+				i <= len && (limit == 0 || GetCount() < limit); 
+				i = j + (int)seplen)
+			{
+				j = str.Find(sep, i);
+				if(j < 0) j = len;
+
+				CStringW s = i < j ? str.Mid(i, j - i) : L"";
+
+				switch(type)
+				{
+				case Min: s.Trim(); // fall through
+				case Def: if(s.IsEmpty()) break; // else fall through
+				case Max: Add(s); break;
+				}
+			}
+		}
+	}
+
+	int Split::GetAtInt(size_t i)
+	{
+		if(i >= GetCount()) throw Exception(_T("Index out of bounds"));
+		return _wtoi(GetAt(i));
+	}
+
+	float Split::GetAtFloat(size_t i)
+	{
+		if(i >= GetCount()) throw Exception(_T("Index out of bounds"));
+		return (float)_wtof(GetAt(i));
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Split.h b/vsfilter/libssf/Split.h
new file mode 100644
index 0000000000000000000000000000000000000000..1d223664ac771f34579ed7ee2fc60ee7177046cc
--- /dev/null
+++ b/vsfilter/libssf/Split.h
@@ -0,0 +1,38 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	class Split : public CAtlArray<CStringW>
+	{
+	public:
+		enum SplitType {Min, Def, Max};
+		Split();
+		Split(LPCWSTR sep, CStringW str, size_t limit = 0, SplitType type = Def);
+		Split(WCHAR sep, CStringW str, size_t limit = 0, SplitType type = Def);
+		operator size_t() {return GetCount();}
+		void DoSplit(LPCWSTR sep, CStringW str, size_t limit, SplitType type);
+		int GetAtInt(size_t i);
+		float GetAtFloat(size_t i);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Stream.cpp b/vsfilter/libssf/Stream.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b7202a952bae88ad994e05dac2f602d88b2a0e6
--- /dev/null
+++ b/vsfilter/libssf/Stream.cpp
@@ -0,0 +1,360 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Stream.h"
+#include <stdio.h>
+
+namespace ssf
+{
+	Stream::Stream()
+		: m_encoding(none)
+		, m_line(0)
+		, m_col(-1)
+	{
+		
+	}
+
+	Stream::~Stream()
+	{
+	}
+
+	void Stream::ThrowError(LPCTSTR fmt, ...)
+	{
+		va_list args;
+		va_start(args, fmt);
+		int len = _vsctprintf(fmt, args) + 1;
+		CString str;
+		if(len > 0) _vstprintf_s(str.GetBufferSetLength(len), len, fmt, args);
+		va_end(args);
+
+		throw Exception(_T("Error (Ln %d Col %d): %s"), m_line+1, m_col+1, str);
+	}
+
+	bool Stream::IsWhiteSpace(int c, LPCWSTR morechars)
+	{
+		return c != 0xa0 && iswspace(c) || morechars && wcschr(morechars, (WCHAR)c);
+	}
+
+	//
+
+	InputStream::InputStream()
+	{
+		
+	}
+
+	InputStream::~InputStream()
+	{
+	}
+
+	int InputStream::NextChar()
+	{
+		if(m_encoding == none)
+		{
+			m_encoding = unknown;
+
+			switch(NextByte())
+			{
+			case 0xef: 
+				if(NextByte() == 0xbb && NextByte() == 0xbf) m_encoding = utf8;
+				break;
+			case 0xff: 
+				if(NextByte() == 0xfe) m_encoding = utf16le;
+				break;
+			case 0xfe:
+				if(NextByte() == 0xff) m_encoding = utf16be;
+				break;
+			}
+		}
+
+		if(m_encoding == unknown)
+		{
+			throw Exception(_T("unknown character encoding, missing BOM"));
+		}
+
+		int i, c;
+
+		int cur = NextByte();
+
+		switch(m_encoding)
+		{
+		case utf8: 
+			for(i = 7; i >= 0 && (cur & (1 << i)); i--);
+			cur &= (1 << i) - 1;
+			while(++i < 7) {c = NextByte(); if(c == EOS) {cur = EOS; break;} cur = (cur << 6) | (c & 0x3f);}
+			break;
+		case utf16le: 
+			c = NextByte();
+			if(c == EOS) {cur = EOS; break;}
+			cur = (c << 8) | cur;
+			break;
+		case utf16be: 
+			c = NextByte();
+			if(c == EOS) {cur = EOS; break;}
+			cur = cur | (c << 8);
+			break;
+		case wchar:
+			break;
+		}
+
+		return cur;
+	}
+
+	int InputStream::PushChar()
+	{
+		int c = NextChar();
+		m_queue.AddTail(c);
+		return c;
+	}
+
+	int InputStream::PopChar()
+	{
+		if(m_queue.IsEmpty()) ThrowError(_T("fatal stream error"));
+
+		int c = m_queue.RemoveHead();
+
+		if(c != EOS)
+		{
+			if(c == '\n') {m_line++; m_col = -1;}
+			m_col++;
+		}
+
+		return c;
+	}
+
+	int InputStream::PeekChar()
+	{
+		while(m_queue.GetCount() < 2) PushChar();
+
+		ASSERT(m_queue.GetCount() == 2);
+
+		if(m_queue.GetHead() == '/' && m_queue.GetTail() == '/')
+		{
+			while(!m_queue.IsEmpty()) PopChar();
+			int c;
+			do {PushChar(); c = PopChar();} while(!(c == '\n' || c == EOS));
+			return PeekChar();
+		}
+		else if(m_queue.GetHead() == '/' && m_queue.GetTail() == '*')
+		{
+			while(!m_queue.IsEmpty()) PopChar();
+			int c1, c2;
+			PushChar();
+			do {c2 = PushChar(); c1 = PopChar();} while(!((c1 == '*' && c2 == '/') || c1 == EOS));
+			PopChar();
+			return PeekChar();
+		}
+
+		return m_queue.GetHead();
+	}
+
+	int InputStream::GetChar()
+	{
+		if(m_queue.GetCount() < 2) PeekChar();
+		return PopChar();
+	}
+
+	int InputStream::SkipWhiteSpace(LPCWSTR morechars)
+	{
+		int c = PeekChar();
+		for(; IsWhiteSpace(c, morechars); c = PeekChar()) 
+			GetChar();
+		return c;
+	}
+
+	// FileInputStream
+
+	FileInputStream::FileInputStream(const TCHAR* fn) 
+		: m_file(NULL)
+	{
+		if(_tfopen_s(&m_file, fn, _T("r")) != 0) ThrowError(_T("cannot open file '%s'"), fn);
+	}
+
+	FileInputStream::~FileInputStream()
+	{
+		if(m_file) {fclose(m_file); m_file = NULL;}
+	}
+
+	int FileInputStream::NextByte()
+	{
+		if(!m_file) ThrowError(_T("file pointer is NULL"));
+		return fgetc(m_file);
+	}
+
+	// MemoryInputStream
+
+	MemoryInputStream::MemoryInputStream(BYTE* pBytes, int len, bool fCopy, bool fFree)
+		: m_pBytes(NULL)
+		, m_pos(0)
+		, m_len(len)
+	{
+		if(fCopy)
+		{
+			m_pBytes = new BYTE[len];
+			if(m_pBytes) memcpy(m_pBytes, pBytes, len);
+			m_fFree = true;
+		}
+		else
+		{
+			m_pBytes = pBytes;
+			m_fFree = fFree;
+		}
+
+		if(!m_pBytes) ThrowError(_T("memory stream pointer is NULL"));
+	}
+
+	MemoryInputStream::~MemoryInputStream()
+	{
+		if(m_fFree) delete [] m_pBytes;
+		m_pBytes = NULL;
+	}
+
+	int MemoryInputStream::NextByte()
+	{
+		if(!m_pBytes) ThrowError(_T("memory stream pointer is NULL"));
+		if(m_pos >= m_len) return Stream::EOS;
+		return (int)m_pBytes[m_pos++];
+	}
+
+	// WCharInputStream
+	
+	WCharInputStream::WCharInputStream(CStringW str)
+		: m_str(str)
+		, m_pos(0)
+	{
+		m_encoding = Stream::wchar; // HACK: it should return real bytes from NextByte (two per wchar_t), but this way it's a lot more simple...
+	}
+
+	int WCharInputStream::NextByte()
+	{
+		if(m_pos >= m_str.GetLength()) return Stream::EOS;
+		return m_str[m_pos++];
+	}
+
+	// OutputStream
+
+	OutputStream::OutputStream(encoding_t e)
+	{
+		m_encoding = e;
+		m_bof = true;
+	}
+
+	OutputStream::~OutputStream()
+	{
+	}
+
+	void OutputStream::PutChar(WCHAR c)
+	{
+		if(m_bof)
+		{
+			m_bof = false;
+
+			switch(m_encoding)
+			{
+			case utf8:
+			case utf16le: 
+			case utf16be:
+				PutChar(0xfeff);
+				break;
+			}
+		}
+
+		switch(m_encoding)
+		{
+		case utf8: 
+			if(0 <= c && c < 0x80) // 0xxxxxxx
+			{
+				NextByte(c);
+			}
+			else if(0x80 <= c && c < 0x800) // 110xxxxx 10xxxxxx
+			{
+				NextByte(0xc0 | ((c<<2)&0x1f));
+				NextByte(0x80 | ((c<<0)&0x3f));
+			}
+			else if(0x800 <= c && c < 0xFFFF) // 1110xxxx 10xxxxxx 10xxxxxx
+			{
+				NextByte(0xe0 | ((c<<4)&0x0f));
+				NextByte(0x80 | ((c<<2)&0x3f));
+				NextByte(0x80 | ((c<<0)&0x3f));
+			}
+			else
+			{
+				NextByte('?');
+			}
+			break;
+		case utf16le:
+			NextByte(c & 0xff);
+			NextByte((c >> 8) & 0xff);
+			break;
+		case utf16be: 
+			NextByte((c >> 8) & 0xff);
+			NextByte(c & 0xff);
+			break;
+		case wchar:
+			NextByte(c);
+			break;
+		}
+	}
+
+	void OutputStream::PutString(LPCWSTR fmt, ...)
+	{
+		CStringW str;
+
+		va_list args;
+		va_start(args, fmt);
+		int len = _vscwprintf(fmt, args) + 1;
+		if(len > 0) vswprintf_s(str.GetBufferSetLength(len), len, fmt, args);
+		va_end(args);
+
+		LPCWSTR s = str;
+		while(*s) PutChar(*s++);
+	}
+
+	// WCharOutputStream
+
+	WCharOutputStream::WCharOutputStream()
+		: OutputStream(wchar)
+	{
+	}
+
+	void WCharOutputStream::NextByte(int b)
+	{
+		m_str += (WCHAR)b;
+	}
+
+	// DebugOutputStream
+
+	DebugOutputStream::DebugOutputStream()
+		: OutputStream(wchar)
+	{
+	}
+
+	DebugOutputStream::~DebugOutputStream()
+	{
+		TRACE(_T("%s\n"), m_str);
+	}
+
+	void DebugOutputStream::NextByte(int b)
+	{
+		if(b == '\n') {TRACE(_T("%s\n"), m_str); m_str.Empty();}
+		else if(b != '\r') m_str += (WCHAR)b;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Stream.h b/vsfilter/libssf/Stream.h
new file mode 100644
index 0000000000000000000000000000000000000000..e093c0eb95b8ecbaafe3ff3e4ea27c934a97128c
--- /dev/null
+++ b/vsfilter/libssf/Stream.h
@@ -0,0 +1,143 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "Exception.h"
+
+namespace ssf
+{
+	class Stream
+	{
+	public:
+		enum {EOS = -1};
+		enum encoding_t {none, unknown, utf8, utf16le, utf16be, wchar};
+
+	protected:
+		int m_line, m_col;
+		encoding_t m_encoding;
+
+	public:
+		Stream();
+		virtual ~Stream();
+
+		static bool IsWhiteSpace(int c, LPCWSTR morechars = NULL);
+
+		void ThrowError(LPCTSTR fmt, ...);
+	};
+
+	class InputStream : public Stream
+	{
+		CAtlList<int> m_queue;
+		int PushChar(), PopChar();
+
+		int NextChar();
+
+	protected:
+		virtual int NextByte() = 0;
+
+	public:
+		InputStream();
+		~InputStream();
+
+		int PeekChar(), GetChar();
+
+		int SkipWhiteSpace(LPCWSTR morechars = NULL);
+	};
+
+	class FileInputStream : public InputStream
+	{
+		FILE* m_file;
+
+	protected:
+		int NextByte();
+
+	public:
+		FileInputStream(const TCHAR* fn);
+		~FileInputStream();
+	};
+
+	class MemoryInputStream : public InputStream
+	{
+		BYTE* m_pBytes;
+		int m_pos, m_len;
+		bool m_fFree;
+
+	protected:
+		int NextByte();
+
+	public:
+		MemoryInputStream(BYTE* pBytes, int len, bool fCopy, bool fFree);
+		~MemoryInputStream();
+	};
+
+	class WCharInputStream : public InputStream
+	{
+		CStringW m_str;
+		int m_pos;
+
+	protected:
+		int NextByte();
+
+	public:
+		WCharInputStream(CStringW str);
+	};
+
+	class OutputStream : public Stream
+	{
+		bool m_bof;
+
+	protected:
+		virtual void NextByte(int b) = 0;
+
+	public:
+		OutputStream(encoding_t e);
+		virtual ~OutputStream();
+
+		void PutChar(WCHAR c);
+		void PutString(LPCWSTR fmt, ...);
+	};
+
+	class WCharOutputStream : public OutputStream
+	{
+		CStringW m_str;
+
+	protected:
+		void NextByte(int b);
+
+	public:
+		WCharOutputStream();
+
+		const CStringW& GetString() {return m_str;}
+	};
+
+	class DebugOutputStream : public OutputStream
+	{
+		CStringW m_str;
+
+	protected:
+		void NextByte(int b);
+
+	public:
+		DebugOutputStream();
+		~DebugOutputStream();
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/StringMap.cpp b/vsfilter/libssf/StringMap.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..38ad7971d8d5a415f1713249a4e60c7c7a27a497
--- /dev/null
+++ b/vsfilter/libssf/StringMap.cpp
@@ -0,0 +1,28 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Exception.h"
+
+namespace ssf
+{
+
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/StringMap.h b/vsfilter/libssf/StringMap.h
new file mode 100644
index 0000000000000000000000000000000000000000..f8e41c8fd33218e0b2c5276ca35b78992639f6f4
--- /dev/null
+++ b/vsfilter/libssf/StringMap.h
@@ -0,0 +1,46 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+namespace ssf
+{
+	template <class T = CString, class S = CString> 
+	class StringMap : public CAtlMap<S, T, CStringElementTraits<S> >
+	{
+	public:
+		StringMap() {}
+		StringMap(const StringMap& s2t) {*this = s2t;}
+		StringMap& operator = (const StringMap& s2t)
+		{
+			RemoveAll();
+			POSITION pos = s2t.GetStartPosition();
+			while(pos) {const StringMap::CPair* p = s2t.GetNext(pos); SetAt(p->m_key, p->m_value);}
+			return *this;
+		}
+	};
+
+	template <class T = CStringA, class S = CStringA> 
+	class StringMapA : public StringMap<T, S> {};
+
+	template <class T = CStringW, class S = CStringW> 
+	class StringMapW : public StringMap<T, S> {};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Subtitle.cpp b/vsfilter/libssf/Subtitle.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..585af10aeb5d3ffe0b4f798352b68fc205897e2e
--- /dev/null
+++ b/vsfilter/libssf/Subtitle.cpp
@@ -0,0 +1,701 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "Subtitle.h"
+#include "Split.h"
+#include <math.h>
+
+namespace ssf
+{
+	struct Subtitle::n2n_t Subtitle::m_n2n;
+
+	Subtitle::Subtitle(File* pFile) 
+		: m_pFile(pFile)
+		, m_animated(false)
+	{
+		if(m_n2n.align[0].IsEmpty())
+		{
+			m_n2n.align[0][L"left"] = 0;
+			m_n2n.align[0][L"center"] = 0.5;
+			m_n2n.align[0][L"middle"] = 0.5;
+			m_n2n.align[0][L"right"] = 1;
+		}
+		
+		if(m_n2n.align[1].IsEmpty())
+		{
+			m_n2n.align[1][L"top"] = 0;
+			m_n2n.align[1][L"middle"] = 0.5;
+			m_n2n.align[1][L"center"] = 0.5;
+			m_n2n.align[1][L"bottom"] = 1;
+		}
+		
+		if(m_n2n.weight.IsEmpty())
+		{
+			m_n2n.weight[L"thin"] = FW_THIN;
+			m_n2n.weight[L"normal"] = FW_NORMAL;
+			m_n2n.weight[L"bold"] = FW_BOLD;
+		}
+
+		if(m_n2n.transition.IsEmpty())
+		{
+			m_n2n.transition[L"start"] = 0;
+			m_n2n.transition[L"stop"] = 1e10;
+			m_n2n.transition[L"linear"] = 1;
+		}
+	}
+
+	Subtitle::~Subtitle() 
+	{
+	}
+
+	bool Subtitle::Parse(Definition* pDef, float start, float stop, float at)
+	{
+		ASSERT(m_pFile && pDef);
+
+		m_name = pDef->m_name;
+
+		m_text.RemoveAll();
+
+		m_time.start = start;
+		m_time.stop = stop;
+
+		at -= start;
+
+		Fill::gen_id = 0;
+
+		m_pFile->Commit();
+
+		try
+		{
+			Definition& frame = (*pDef)[L"frame"];
+
+			m_frame.reference = frame[L"reference"];
+			m_frame.resolution.cx = frame[L"resolution"][L"cx"];
+			m_frame.resolution.cy = frame[L"resolution"][L"cy"];
+
+			Definition& direction = (*pDef)[L"direction"];
+
+			m_direction.primary = direction[L"primary"];
+			m_direction.secondary = direction[L"secondary"];
+
+			m_wrap = (*pDef)[L"wrap"];
+
+			m_layer = (*pDef)[L"layer"];
+
+			Style style;
+			GetStyle(&(*pDef)[L"style"], style);
+
+			StringMapW<float> offset;
+			Definition& block = (*pDef)[L"@"];
+			Parse(WCharInputStream((LPCWSTR)block), style, at, offset, dynamic_cast<Reference*>(block.m_parent));
+
+			// TODO: trimming should be done by the renderer later, after breaking the words into lines
+
+			while(!m_text.IsEmpty() && (m_text.GetHead().str == Text::SP || m_text.GetHead().str == Text::LSEP))
+				m_text.RemoveHead();
+
+			while(!m_text.IsEmpty() && (m_text.GetTail().str == Text::SP || m_text.GetTail().str == Text::LSEP))
+				m_text.RemoveTail();
+
+			for(POSITION pos = m_text.GetHeadPosition(); pos; m_text.GetNext(pos))
+			{
+				if(m_text.GetAt(pos).str == Text::LSEP)
+				{
+					POSITION prev = pos;
+					m_text.GetPrev(prev);
+
+					while(prev && m_text.GetAt(prev).str == Text::SP)
+					{
+						POSITION tmp = prev;
+						m_text.GetPrev(prev);
+						m_text.RemoveAt(tmp);
+					}
+
+					POSITION next = pos;
+					m_text.GetNext(next);
+
+					while(next && m_text.GetAt(next).str == Text::SP)
+					{
+						POSITION tmp = next;
+						m_text.GetNext(next);
+						m_text.RemoveAt(tmp);
+					}
+				}
+			}
+		}
+		catch(Exception& e)
+		{
+			TRACE(_T("%s"), e.ToString());
+			return false;
+		}
+
+		m_pFile->Rollback();
+
+		return true;
+	}
+
+	void Subtitle::GetStyle(Definition* pDef, Style& style)
+	{
+		style.placement.pos.x = 0;
+		style.placement.pos.y = 0;
+		style.placement.pos.auto_x = true;
+		style.placement.pos.auto_y = true;
+
+		style.placement.org.x = 0;
+		style.placement.org.y = 0;
+		style.placement.org.auto_x = true;
+		style.placement.org.auto_y = true;
+
+		Rect frame = {0, m_frame.resolution.cx, m_frame.resolution.cy, 0};
+
+		style.placement.clip.t = -1;
+		style.placement.clip.r = -1;
+		style.placement.clip.b = -1;
+		style.placement.clip.l = -1;
+
+		//
+
+		style.linebreak = (*pDef)[L"linebreak"];
+
+		Definition& placement = (*pDef)[L"placement"];
+
+		Definition& clip = placement[L"clip"];
+
+		if(clip.IsValue(Definition::string))
+		{
+			CStringW str = clip;
+
+			if(str == L"frame") style.placement.clip = frame;
+			// else ?
+		}
+		else
+		{
+			if(clip[L"t"].IsValue()) style.placement.clip.t = clip[L"t"];
+			if(clip[L"r"].IsValue()) style.placement.clip.r = clip[L"r"];
+			if(clip[L"b"].IsValue()) style.placement.clip.b = clip[L"b"];
+			if(clip[L"l"].IsValue()) style.placement.clip.l = clip[L"l"];
+		}
+
+		StringMapW<float> n2n_margin[2];
+
+		n2n_margin[0][L"top"] = 0;
+		n2n_margin[0][L"right"] = 0;
+		n2n_margin[0][L"bottom"] = frame.b - frame.t;
+		n2n_margin[0][L"left"] = frame.r - frame.l;
+		n2n_margin[1][L"top"] = frame.b - frame.t;
+		n2n_margin[1][L"right"] = frame.r - frame.l;
+		n2n_margin[1][L"bottom"] = 0;
+		n2n_margin[1][L"left"] = 0;
+
+		placement[L"margin"][L"t"].GetAsNumber(style.placement.margin.t, &n2n_margin[0]);
+		placement[L"margin"][L"r"].GetAsNumber(style.placement.margin.r, &n2n_margin[0]);
+		placement[L"margin"][L"b"].GetAsNumber(style.placement.margin.b, &n2n_margin[1]);
+		placement[L"margin"][L"l"].GetAsNumber(style.placement.margin.l, &n2n_margin[1]);
+
+		placement[L"align"][L"h"].GetAsNumber(style.placement.align.h, &m_n2n.align[0]);
+		placement[L"align"][L"v"].GetAsNumber(style.placement.align.v, &m_n2n.align[1]);
+
+		if(placement[L"pos"][L"x"].IsValue()) {style.placement.pos.x = placement[L"pos"][L"x"]; style.placement.pos.auto_x = false;}
+		if(placement[L"pos"][L"y"].IsValue()) {style.placement.pos.y = placement[L"pos"][L"y"]; style.placement.pos.auto_y = false;}
+
+		placement[L"offset"][L"x"].GetAsNumber(style.placement.offset.x);
+		placement[L"offset"][L"y"].GetAsNumber(style.placement.offset.y);
+
+		style.placement.angle.x = placement[L"angle"][L"x"];
+		style.placement.angle.y = placement[L"angle"][L"y"];
+		style.placement.angle.z = placement[L"angle"][L"z"];
+
+		if(placement[L"org"][L"x"].IsValue()) {style.placement.org.x = placement[L"org"][L"x"]; style.placement.org.auto_x = false;}
+		if(placement[L"org"][L"y"].IsValue()) {style.placement.org.y = placement[L"org"][L"y"]; style.placement.org.auto_y = false;}
+
+		style.placement.path = placement[L"path"];
+
+		Definition& font = (*pDef)[L"font"];
+
+		style.font.face = font[L"face"];
+		style.font.size = font[L"size"];
+		font[L"weight"].GetAsNumber(style.font.weight, &m_n2n.weight);
+		style.font.color.a = font[L"color"][L"a"];
+		style.font.color.r = font[L"color"][L"r"];
+		style.font.color.g = font[L"color"][L"g"];
+		style.font.color.b = font[L"color"][L"b"];
+		style.font.underline = font[L"underline"];
+		style.font.strikethrough = font[L"strikethrough"];
+		style.font.italic = font[L"italic"];
+		style.font.spacing = font[L"spacing"];
+		style.font.scale.cx = font[L"scale"][L"cx"];
+		style.font.scale.cy = font[L"scale"][L"cy"];
+		style.font.kerning = font[L"kerning"];
+
+		Definition& background = (*pDef)[L"background"];
+
+		style.background.color.a = background[L"color"][L"a"];
+		style.background.color.r = background[L"color"][L"r"];
+		style.background.color.g = background[L"color"][L"g"];
+		style.background.color.b = background[L"color"][L"b"];
+		style.background.size = background[L"size"];
+		style.background.type = background[L"type"];
+		style.background.blur = background[L"blur"];
+
+		Definition& shadow = (*pDef)[L"shadow"];
+
+		style.shadow.color.a = shadow[L"color"][L"a"];
+		style.shadow.color.r = shadow[L"color"][L"r"];
+		style.shadow.color.g = shadow[L"color"][L"g"];
+		style.shadow.color.b = shadow[L"color"][L"b"];
+		style.shadow.depth = shadow[L"depth"];
+		style.shadow.angle = shadow[L"angle"];
+		style.shadow.blur = shadow[L"blur"];
+
+		Definition& fill = (*pDef)[L"fill"];
+
+		style.fill.color.a = fill[L"color"][L"a"];
+		style.fill.color.r = fill[L"color"][L"r"];
+		style.fill.color.g = fill[L"color"][L"g"];
+		style.fill.color.b = fill[L"color"][L"b"];
+		style.fill.width = fill[L"width"];
+	}
+
+	float Subtitle::GetMixWeight(Definition* pDef, float at, StringMapW<float>& offset, int default_id)
+	{
+		float t = 1;
+
+		try
+		{
+			StringMapW<float> n2n;
+
+			n2n[L"start"] = 0;
+			n2n[L"stop"] = m_time.stop - m_time.start;
+
+			Definition::Time time;
+			if(pDef->GetAsTime(time, offset, &n2n, default_id) && time.start.value < time.stop.value)
+			{
+				t = (at - time.start.value) / (time.stop.value - time.start.value);
+
+				float u = t;
+
+				if(t < 0) t = 0;
+				else if(t >= 1) t = 0.99999f; // doh
+
+				if((*pDef)[L"loop"].IsValue()) t *= (float)(*pDef)[L"loop"];
+
+				CStringW direction = (*pDef)[L"direction"].IsValue() ? (*pDef)[L"direction"] : L"fw";
+				if(direction == L"fwbw" || direction == L"bwfw") t *= 2;
+
+				float n;
+				t = modf(t, &n);
+
+				if(direction == L"bw" 
+				|| direction == L"fwbw" && ((int)n & 1)
+				|| direction == L"bwfw" && !((int)n & 1)) 
+					t = 1 - t;
+
+				float accel = 1;
+
+				if((*pDef)[L"transition"].IsValue())
+				{
+					Definition::Number<float> n;
+					(*pDef)[L"transition"].GetAsNumber(n, &m_n2n.transition);
+					if(n.value >= 0) accel = n.value;
+				}
+
+				if(t == 0.99999f) t = 1;
+
+				if(u >= 0 && u < 1)
+				{
+					t = accel == 0 ? 1 : 
+						accel == 1 ? t : 
+						accel >= 1e10 ? 0 :
+						pow(t, accel);
+				}
+			}
+		}
+		catch(Exception&)
+		{
+		}
+
+		return t;
+	}
+
+	template<class T> 
+	bool Subtitle::MixValue(Definition& def, T& value, float t)
+	{
+		StringMapW<T> n2n;
+		return MixValue(def, value, t, &n2n);
+	}
+
+	template<> 
+	bool Subtitle::MixValue(Definition& def, float& value, float t)
+	{
+		StringMapW<float> n2n;
+		return MixValue(def, value, t, &n2n);
+	}
+
+	template<class T> 
+	bool Subtitle::MixValue(Definition& def, T& value, float t, StringMapW<T>* n2n)
+	{
+		if(!def.IsValue()) return false;
+
+		if(t >= 0.5)
+		{
+			if(n2n && def.IsValue(Definition::string))
+			{
+				if(StringMapW<T>::CPair* p = n2n->Lookup(def))
+				{
+					value = p->m_value;
+					return true;
+				}
+			}
+
+			value = (T)def;
+		}
+
+		return true;
+	}
+
+	template<> 
+	bool Subtitle::MixValue(Definition& def, float& value, float t, StringMapW<float>* n2n)
+	{
+		if(!def.IsValue()) return false;
+
+		if(t > 0)
+		{
+			if(n2n && def.IsValue(Definition::string))
+			{
+				if(StringMap<float, CStringW>::CPair* p = n2n->Lookup(def))
+				{
+					value += (p->m_value - value) * t;
+					return true;
+				}
+			}
+
+			value += ((float)def - value) * t;
+		}
+
+		return true;
+	}
+
+	template<>
+	bool Subtitle::MixValue(Definition& def, Path& src, float t)
+	{
+		if(!def.IsValue(Definition::string)) return false;
+
+		if(t >= 1)
+		{
+			src = (LPCWSTR)def;
+		}
+		else if(t > 0)
+		{
+			Path dst = (LPCWSTR)def;
+
+			if(src.GetCount() == dst.GetCount())
+			{
+				for(size_t i = 0, j = src.GetCount(); i < j; i++)
+				{
+					Point& s = src[i];
+					const Point& d = dst[i];
+					s.x += (d.x - s.x) * t;
+					s.y += (d.y - s.y) * t;
+				}
+			}
+		}
+
+		return true;
+	}
+
+	void Subtitle::MixStyle(Definition* pDef, Style& dst, float t)
+	{
+		const Style src = dst;
+
+		if(t <= 0) return;
+		else if(t > 1) t = 1;
+
+		MixValue((*pDef)[L"linebreak"], dst.linebreak, t);
+
+		Definition& placement = (*pDef)[L"placement"];
+
+		MixValue(placement[L"clip"][L"t"], dst.placement.clip.t, t);
+		MixValue(placement[L"clip"][L"r"], dst.placement.clip.r, t);
+		MixValue(placement[L"clip"][L"b"], dst.placement.clip.b, t);
+		MixValue(placement[L"clip"][L"l"], dst.placement.clip.l, t);
+		MixValue(placement[L"align"][L"h"], dst.placement.align.h, t, &m_n2n.align[0]);
+		MixValue(placement[L"align"][L"v"], dst.placement.align.v, t, &m_n2n.align[1]);
+		dst.placement.pos.auto_x = !MixValue(placement[L"pos"][L"x"], dst.placement.pos.x, dst.placement.pos.auto_x ? 1 : t);
+		dst.placement.pos.auto_y = !MixValue(placement[L"pos"][L"y"], dst.placement.pos.y, dst.placement.pos.auto_y ? 1 : t);
+		MixValue(placement[L"offset"][L"x"], dst.placement.offset.x, t);
+		MixValue(placement[L"offset"][L"y"], dst.placement.offset.y, t);
+		MixValue(placement[L"angle"][L"x"], dst.placement.angle.x, t);
+		MixValue(placement[L"angle"][L"y"], dst.placement.angle.y, t);
+		MixValue(placement[L"angle"][L"z"], dst.placement.angle.z, t);
+		dst.placement.org.auto_x = !MixValue(placement[L"org"][L"x"], dst.placement.org.x, dst.placement.org.auto_x ? 1 : t);
+		dst.placement.org.auto_y = !MixValue(placement[L"org"][L"y"], dst.placement.org.y, dst.placement.org.auto_y ? 1 : t);
+		MixValue(placement[L"path"], dst.placement.path, t);
+
+		Definition& font = (*pDef)[L"font"];
+
+		MixValue(font[L"face"], dst.font.face, t);
+		MixValue(font[L"size"], dst.font.size, t);
+		MixValue(font[L"weight"], dst.font.weight, t, &m_n2n.weight);
+		MixValue(font[L"color"][L"a"], dst.font.color.a, t);
+		MixValue(font[L"color"][L"r"], dst.font.color.r, t);
+		MixValue(font[L"color"][L"g"], dst.font.color.g, t);
+		MixValue(font[L"color"][L"b"], dst.font.color.b, t);
+		MixValue(font[L"underline"], dst.font.underline, t);
+		MixValue(font[L"strikethrough"], dst.font.strikethrough, t);
+		MixValue(font[L"italic"], dst.font.italic, t);
+		MixValue(font[L"spacing"], dst.font.spacing, t);
+		MixValue(font[L"scale"][L"cx"], dst.font.scale.cx, t);
+		MixValue(font[L"scale"][L"cy"], dst.font.scale.cy, t);
+		MixValue(font[L"kerning"], dst.font.kerning, t);
+
+		Definition& background = (*pDef)[L"background"];
+
+		MixValue(background[L"color"][L"a"], dst.background.color.a, t);
+		MixValue(background[L"color"][L"r"], dst.background.color.r, t);
+		MixValue(background[L"color"][L"g"], dst.background.color.g, t);
+		MixValue(background[L"color"][L"b"], dst.background.color.b, t);
+		MixValue(background[L"size"], dst.background.size, t);
+		MixValue(background[L"type"], dst.background.type, t);
+		MixValue(background[L"blur"], dst.background.blur, t);
+
+		Definition& shadow = (*pDef)[L"shadow"];
+
+		MixValue(shadow[L"color"][L"a"], dst.shadow.color.a, t);
+		MixValue(shadow[L"color"][L"r"], dst.shadow.color.r, t);
+		MixValue(shadow[L"color"][L"g"], dst.shadow.color.g, t);
+		MixValue(shadow[L"color"][L"b"], dst.shadow.color.b, t);
+		MixValue(shadow[L"depth"], dst.shadow.depth, t);
+		MixValue(shadow[L"angle"], dst.shadow.angle, t);
+		MixValue(shadow[L"blur"], dst.shadow.blur, t);
+
+		Definition& fill = (*pDef)[L"fill"];
+
+		MixValue(fill[L"color"][L"a"], dst.fill.color.a, t);
+		MixValue(fill[L"color"][L"r"], dst.fill.color.r, t);
+		MixValue(fill[L"color"][L"g"], dst.fill.color.g, t);
+		MixValue(fill[L"color"][L"b"], dst.fill.color.b, t);
+		MixValue(fill[L"width"], dst.fill.width, t);
+
+		if(fill.m_priority >= PNormal) // this assumes there is no way to set low priority inline overrides
+		{
+			if(dst.fill.id > 0) throw Exception(_T("cannot apply fill more than once on the same text"));
+			dst.fill.id = ++Fill::gen_id;
+		}
+	}
+
+	void Subtitle::Parse(InputStream& s, Style style, float at, StringMapW<float> offset, Reference* pParentRef)
+	{
+		Text text;
+		text.style = style;
+
+		for(int c = s.PeekChar(); c != Stream::EOS; c = s.PeekChar())
+		{
+			s.GetChar();
+
+			if(c == '[')
+			{
+				AddText(text);
+
+				style = text.style;
+
+				StringMapW<float> inneroffset = offset;
+
+				int default_id = 0;
+
+				do
+				{
+					Definition* pDef = m_pFile->CreateDef(pParentRef);
+
+					m_pFile->ParseRefs(s, pDef, L",;]");
+
+					ASSERT(pDef->IsType(L"style") || pDef->IsTypeUnknown());
+
+					if((*pDef)[L"time"][L"start"].IsValue() && (*pDef)[L"time"][L"stop"].IsValue())
+					{
+						m_animated = true;
+					}
+
+					float t = GetMixWeight(pDef, at, offset, ++default_id);
+					MixStyle(pDef, style, t);
+
+					if((*pDef)[L"@"].IsValue())
+					{
+						Parse(WCharInputStream((LPCWSTR)(*pDef)[L"@"]), style, at, offset, pParentRef);
+					}
+
+					s.SkipWhiteSpace();
+					c = s.GetChar();
+				}
+				while(c == ',' || c == ';');
+
+				if(c != ']') s.ThrowError(_T("unterminated override"));
+
+				bool fWhiteSpaceNext = s.IsWhiteSpace(s.PeekChar());
+				c = s.SkipWhiteSpace();
+
+				if(c == '{') 
+				{
+					s.GetChar();
+					Parse(s, style, at, inneroffset, pParentRef);
+				}
+				else 
+				{
+					if(fWhiteSpaceNext) text.str += (WCHAR)Text::SP;
+					text.style = style;
+				}
+			}
+			else if(c == ']')
+			{
+				s.ThrowError(_T("unexpected ] found"));
+			}
+			else if(c == '{')
+			{
+				AddText(text);
+				Parse(s, text.style, at, offset, pParentRef);
+			}
+			else if(c == '}')
+			{
+				break;
+			}
+			else
+			{
+				if(c == '\\')
+				{
+					c = s.GetChar();
+					if(c == Stream::EOS) break;
+					else if(c == 'n') {AddText(text); text.str = (WCHAR)Text::LSEP; AddText(text); continue;}
+					else if(c == 'h') c = Text::NBSP;
+				}
+
+				AddChar(text, (WCHAR)c);
+			}
+		}
+
+		AddText(text);
+	}
+
+	void Subtitle::AddChar(Text& t, WCHAR c)
+	{
+		bool f1 = !t.str.IsEmpty() && Stream::IsWhiteSpace(t.str[t.str.GetLength()-1]);
+		bool f2 = Stream::IsWhiteSpace(c);
+		if(f2) c = Text::SP;
+		if(!f1 || !f2) t.str += (WCHAR)c;
+	}
+
+	void Subtitle::AddText(Text& t)
+	{
+		if(t.str.IsEmpty()) return;
+
+		Split sa(' ', t.str, 0, Split::Max);
+
+		for(size_t i = 0, n = sa; i < n; i++)
+		{
+			CStringW str = sa[i];
+
+			if(!str.IsEmpty())
+			{
+				t.str = str;
+				m_text.AddTail(t);
+			}
+
+			if(i < n-1 && (m_text.IsEmpty() || m_text.GetTail().str != Text::SP))
+			{
+				t.str = (WCHAR)Text::SP;
+				m_text.AddTail(t);
+			}
+		}
+		
+		t.str.Empty();
+	}
+
+	//
+
+	unsigned int Fill::gen_id = 0;
+
+	Color::operator DWORD()
+	{
+		DWORD c = 
+			(min(max((DWORD)b, 0), 255) <<  0) |
+			(min(max((DWORD)g, 0), 255) <<  8) |
+			(min(max((DWORD)r, 0), 255) << 16) |
+			(min(max((DWORD)a, 0), 255) << 24);
+
+		return c;
+	}
+
+	Path& Path::operator = (LPCWSTR str)
+	{
+		Split s(' ', str);
+
+		SetCount(s/2);
+
+		for(size_t i = 0, j = GetCount(); i < j; i++)
+		{
+			Point p;
+			p.x = s.GetAtFloat(i*2+0);
+			p.y = s.GetAtFloat(i*2+1);
+			SetAt(i, p);
+		}
+
+		return *this;
+	}
+
+	CStringW Path::ToString()
+	{
+		CStringW ret;
+
+		for(size_t i = 0, j = GetCount(); i < j; i++)
+		{
+			const Point& p = GetAt(i);
+
+			CStringW str;
+			str.Format(L"%f %f ", p.x, p.y);
+			ret += str;
+		}
+
+		return ret;
+	}
+
+	bool Style::IsSimilar(const Style& s)
+	{
+		return 
+		   font.color.r == s.font.color.r
+		&& font.color.g == s.font.color.g
+		&& font.color.b == s.font.color.b
+		&& font.color.a == s.font.color.a
+		&& background.color.r == s.background.color.r
+		&& background.color.g == s.background.color.g
+		&& background.color.b == s.background.color.b
+		&& background.color.a == s.background.color.a
+		&& background.size == s.background.size
+		&& background.type == s.background.type
+		&& background.blur == s.background.blur
+		&& shadow.color.r == s.shadow.color.r
+		&& shadow.color.g == s.shadow.color.g
+		&& shadow.color.b == s.shadow.color.b
+		&& shadow.color.a == s.shadow.color.a
+		&& shadow.depth == s.shadow.depth
+		&& shadow.angle == s.shadow.angle
+		&& shadow.blur == s.shadow.blur
+		&& fill.id == s.fill.id;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/Subtitle.h b/vsfilter/libssf/Subtitle.h
new file mode 100644
index 0000000000000000000000000000000000000000..a15ef8ef5bd132b5a991a051348ae2dba9ac5791
--- /dev/null
+++ b/vsfilter/libssf/Subtitle.h
@@ -0,0 +1,138 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "File.h"
+
+namespace ssf
+{
+	struct Point {float x, y;};
+	struct PointAuto : public Point {bool auto_x, auto_y;};
+	struct Size {float cx, cy;};
+	struct Rect {float t, r, b, l;};
+	struct Align {float v, h;};
+	struct Angle {float x, y, z;};
+	struct Color {float a, r, g, b; operator DWORD(); void operator = (DWORD c);};
+	struct Frame {CStringW reference; Size resolution;};
+	struct Direction {CStringW primary, secondary;};
+	struct Time {float start, stop;};
+	struct Background {Color color; float size, blur; CStringW type;};
+	struct Shadow {Color color; float depth, angle, blur;};
+
+	class Path : public CAtlArray<Point>
+	{
+	public: 
+		Path() {} 
+		Path(const Path& path) {*this = path;} 
+		Path& operator = (const Path& path) {Copy(path); return *this;} 
+		Path(LPCWSTR str) {*this = str;} 
+		Path& operator = (LPCWSTR str);
+		CStringW ToString();
+	};
+
+	struct Placement 
+	{
+		Rect clip, margin; 
+		Align align; 
+		PointAuto pos; 
+		Point offset;
+		Angle angle; 
+		PointAuto org;
+		Path path;
+	};
+
+	struct Font
+	{
+		CStringW face;
+		float size, weight;
+		Color color;
+		bool underline, strikethrough, italic;
+		float spacing;
+		Size scale;
+		bool kerning;
+	};
+
+	struct Fill
+	{
+		static unsigned int gen_id;
+		int id;
+		Color color;
+		float width;
+		struct Fill() : id(0) {}
+	};
+
+	struct Style
+	{
+		CStringW linebreak;
+		Placement placement;
+		Font font;
+		Background background;
+		Shadow shadow;
+		Fill fill;
+
+		bool IsSimilar(const Style& s);
+	};
+
+	struct Text
+	{
+		enum {SP = 0x20, NBSP = 0xa0, LSEP = 0x0a};
+		Style style;
+		CStringW str;
+	};
+
+	class Subtitle
+	{
+		static struct n2n_t {StringMapW<float> align[2], weight, transition;} m_n2n;
+
+		File* m_pFile;
+
+		void GetStyle(Definition* pDef, Style& style);
+		float GetMixWeight(Definition* pDef, float at, StringMapW<float>& offset, int default_id);
+		template<class T> bool MixValue(Definition& def, T& value, float t);
+		template<> bool MixValue(Definition& def, float& value, float t);
+		template<class T> bool MixValue(Definition& def, T& value, float t, StringMapW<T>* n2n);
+		template<> bool MixValue(Definition& def, float& value, float t, StringMapW<float>* n2n);
+		template<> bool MixValue(Definition& def, Path& value, float t);
+		void MixStyle(Definition* pDef, Style& dst, float t);
+
+		void Parse(InputStream& s, Style style, float at, StringMapW<float> offset, Reference* pParentRef);
+
+		void AddChar(Text& t, WCHAR c);
+		void AddText(Text& t);
+
+	public:
+		CStringW m_name;
+		bool m_animated;
+		Frame m_frame;
+		Direction m_direction;
+		CStringW m_wrap;
+		float m_layer;
+		Time m_time;
+		CAtlList<Text> m_text;
+
+	public:
+		Subtitle(File* pFile);
+		virtual ~Subtitle();
+
+		bool Parse(Definition* pDef, float start, float stop, float at);
+	};
+};
\ No newline at end of file
diff --git a/vsfilter/libssf/SubtitleFile.cpp b/vsfilter/libssf/SubtitleFile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e8e4461adfc88cc9a22b8fccad962615a21816a
--- /dev/null
+++ b/vsfilter/libssf/SubtitleFile.cpp
@@ -0,0 +1,403 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "SubtitleFile.h"
+
+namespace ssf
+{
+	SubtitleFile::SubtitleFile()
+	{
+	}
+
+	SubtitleFile::~SubtitleFile()
+	{
+	}
+
+	void SubtitleFile::Parse(InputStream& s)
+	{
+		m_segments.RemoveAll();
+
+		__super::Parse(s, s_predef);
+
+		// TODO: check file.format == "ssf" and file.version == 1
+
+		CAtlList<Definition*> defs;
+		GetRootRef()->GetChildDefs(defs, L"subtitle");
+		
+		StringMapW<float> offset;
+
+		POSITION pos = defs.GetHeadPosition();
+		while(pos)
+		{
+			Definition* pDef = defs.GetNext(pos);
+
+			try
+			{
+				Definition::Time time;
+
+				if(pDef->GetAsTime(time, offset) && (*pDef)[L"@"].IsValue())
+				{
+					m_segments.Insert(time.start.value, time.stop.value, pDef);
+				}
+			}
+			catch(Exception&)
+			{
+			}
+		}
+	}
+
+	void SubtitleFile::Append(InputStream& s, float start, float stop, bool fSetTime)
+	{
+		Reference* pRootRef = GetRootRef();
+
+		ParseDefs(s, pRootRef);
+
+		CAtlList<Definition*> defs;
+		GetNewDefs(defs);
+
+		POSITION pos = defs.GetHeadPosition();
+		while(pos)
+		{
+			Definition* pDef = defs.GetNext(pos);
+
+			if(pDef->m_parent == pRootRef && pDef->m_type == L"subtitle" && (*pDef)[L"@"].IsValue())
+			{
+				m_segments.Insert(start, stop, pDef);
+
+				if(fSetTime) 
+				{
+					try
+					{
+						Definition::Time time;
+						StringMapW<float> offset;
+						pDef->GetAsTime(time, offset);
+						if(time.start.value == start && time.stop.value == stop)
+							continue;
+					}
+					catch(Exception&)
+					{
+					}
+
+					CStringW str;
+					str.Format(L"%.3f", start);
+					pDef->SetChildAsNumber(L"time.start", str, L"s");
+					str.Format(L"%.3f", stop);
+					pDef->SetChildAsNumber(L"time.stop", str, L"s");
+				}
+			}
+		}
+
+		Commit();
+	}
+
+	bool SubtitleFile::Lookup(float at, CAutoPtrList<Subtitle>& subs)
+	{
+		if(!subs.IsEmpty()) {ASSERT(0); return false;}
+
+		CAtlList<SegmentItem> sis;
+		m_segments.Lookup(at, sis);
+
+		POSITION pos = sis.GetHeadPosition();
+		while(pos)
+		{
+			SegmentItem& si = sis.GetNext(pos);
+
+			CAutoPtr<Subtitle> s(new Subtitle(this));
+
+			if(s->Parse(si.pDef, si.start, si.stop, at))
+			{
+				for(POSITION pos = subs.GetHeadPosition(); pos; subs.GetNext(pos))
+				{
+					if(s->m_layer < subs.GetAt(pos)->m_layer)
+					{
+						subs.InsertBefore(pos, s);
+						break;
+					}
+				}
+
+				if(s)
+				{
+					subs.AddTail(s);
+				}
+			}
+		}
+
+		return !subs.IsEmpty();
+	}
+
+	//
+
+	SubtitleFile::Segment::Segment(float start, float stop, const SegmentItem* si)
+	{
+		m_start = start;
+		m_stop = stop;
+		if(si) AddTail(*si);
+	}
+
+	SubtitleFile::Segment::Segment(const Segment& s)
+	{
+		*this = s;
+	}
+
+	void SubtitleFile::Segment::operator = (const Segment& s)
+	{
+		m_start = s.m_start; 
+		m_stop = s.m_stop; 
+		RemoveAll(); 
+		AddTailList(&s);
+	}
+
+	//
+
+	void SubtitleFile::SegmentList::RemoveAll()
+	{
+		__super::RemoveAll();
+		m_index.RemoveAll();
+	}
+
+	void SubtitleFile::SegmentList::Insert(float start, float stop, Definition* pDef)
+	{
+		if(start >= stop) {ASSERT(0); return;}
+
+		m_index.RemoveAll();
+
+		SegmentItem si = {pDef, start, stop};
+
+		if(IsEmpty())
+		{
+			AddTail(Segment(start, stop, &si));
+			return;
+		}
+		
+		Segment& head = GetHead();
+		Segment& tail = GetTail();
+		
+		if(start >= tail.m_stop && stop > tail.m_stop)
+		{
+			if(start > tail.m_stop) AddTail(Segment(tail.m_stop, start));
+			AddTail(Segment(start, stop, &si));
+		}
+		else if(start < head.m_start && stop <= head.m_start)
+		{
+			if(stop < head.m_start) AddHead(Segment(stop, head.m_start));
+			AddHead(Segment(start, stop, &si));
+		}
+		else 
+		{
+			if(start < head.m_start)
+			{
+				AddHead(Segment(start, head.m_start, &si));
+				start = head.m_start;
+			}
+
+			if(stop > tail.m_stop)
+			{
+				AddTail(Segment(tail.m_stop, stop, &si));
+				stop = tail.m_stop;
+			}
+
+			for(POSITION pos = GetHeadPosition(); pos; GetNext(pos))
+			{
+				Segment& s = GetAt(pos);
+
+				if(start >= s.m_stop) continue;
+				if(stop <= s.m_start) break;
+
+				if(s.m_start < start && start < s.m_stop)
+				{
+					Segment s2 = s;
+					s2.m_start = start;
+					InsertAfter(pos, s2);
+					s.m_stop = start;
+				}
+				else if(s.m_start == start)
+				{
+					if(stop > s.m_stop)
+					{
+						start = s.m_stop;
+					}
+					else if(stop < s.m_stop)
+					{
+						Segment s2 = s;
+						s2.m_start = stop;
+						InsertAfter(pos, s2);
+						s.m_stop = stop;
+					}
+
+					s.AddTail(si);
+				}
+			}
+		}
+	}
+
+	size_t SubtitleFile::SegmentList::Index(bool fForce)
+	{
+		if(m_index.IsEmpty() || fForce)
+		{
+			m_index.RemoveAll();
+			POSITION pos = GetHeadPosition();
+			while(pos) m_index.Add(&GetNext(pos));
+		}
+
+		return m_index.GetCount();
+	}
+
+	void SubtitleFile::SegmentList::Lookup(float at, CAtlList<SegmentItem>& sis)
+	{
+		sis.RemoveAll();
+
+		size_t k;
+		if(Lookup(at, k)) 
+		{
+			sis.AddTailList(GetSegment(k));
+		}
+	}
+
+	bool SubtitleFile::SegmentList::Lookup(float at, size_t& k)
+	{
+		if(!Index()) return false;
+
+		size_t i = 0, j = m_index.GetCount()-1;
+
+		if(m_index[i]->m_start <= at && at < m_index[j]->m_stop)
+		do
+		{
+			k = (i+j)/2;
+			if(m_index[k]->m_start <= at && at < m_index[k]->m_stop) {return true;}
+			else if(at < m_index[k]->m_start) {if(j == k) k--; j = k;}
+			else if(at >= m_index[k]->m_stop) {if(i == k) k++; i = k;}
+		}
+		while(i <= j);
+
+		return false;
+	}
+
+	const SubtitleFile::Segment* SubtitleFile::SegmentList::GetSegment(size_t k)
+	{
+		return 0 <= k && k < m_index.GetCount() ? m_index[k] : NULL;
+	}
+
+	// TODO: this should be overridable from outside
+
+	LPCWSTR SubtitleFile::s_predef = 
+		L"color#white {a: 255; r: 255; g: 255; b: 255;}; \n"
+		L"color#black {a: 255; r: 0; g: 0; b: 0;}; \n"
+		L"color#gray {a: 255; r: 128; g: 128; b: 128;}; \n" 
+		L"color#red {a: 255; r: 255; g: 0; b: 0;}; \n"
+		L"color#green {a: 255; r: 0; g: 255; b: 0;}; \n"
+		L"color#blue {a: 255; r: 0; g: 0; b: 255;}; \n"
+		L"color#cyan {a: 255; r: 0; g: 255; b: 255;}; \n"
+		L"color#yellow {a: 255; r: 255; g: 255; b: 0;}; \n"
+		L"color#magenta {a: 255; r: 255; g: 0; b: 255;}; \n"
+		L" \n"
+		L"align#topleft {v: \"top\"; h: \"left\";}; \n"
+		L"align#topcenter {v: \"top\"; h: \"center\";}; \n"
+		L"align#topright {v: \"top\"; h: \"right\";}; \n"
+		L"align#middleleft {v: \"middle\"; h: \"left\";}; \n"
+		L"align#middlecenter {v: \"middle\"; h: \"center\";}; \n"
+		L"align#middleright {v: \"middle\"; h: \"right\";}; \n"
+		L"align#bottomleft {v: \"bottom\"; h: \"left\";}; \n"
+		L"align#bottomcenter {v: \"bottom\"; h: \"center\";}; \n"
+		L"align#bottomright {v: \"bottom\"; h: \"right\";}; \n"
+		L" \n"
+		L"time#time {scale: 1;}; \n"
+		L"time#startstop {start: \"start\"; stop: \"stop\";}; \n"
+		L" \n"
+		L"#b {font.weight: \"bold\"}; \n"
+		L"#i {font.italic: \"true\"}; \n"
+		L"#u {font.underline: \"true\"}; \n"
+		L"#s {font.strikethrough: \"true\"}; \n"
+		L" \n"
+		L"#nobr {linebreak: \"none\"}; \n"
+		L" \n"
+		L"subtitle#subtitle \n"
+		L"{ \n"
+		L"	frame \n"
+		L"	{ \n"
+		L"		reference: \"video\"; \n"
+		L"		resolution: {cx: 640; cy: 480;}; \n"
+		L"	}; \n"
+		L" \n"
+		L"	direction \n"
+		L"	{ \n"
+		L"		primary: \"right\"; \n"
+		L"		secondary: \"down\"; \n"
+		L"	}; \n"
+		L" \n"
+		L"	wrap: \"normal\"; \n"
+		L" \n"
+		L"	layer: 0; \n"
+		L" \n"
+		L"	style \n"
+		L"	{ \n"
+		L"		linebreak: \"word\"; \n"
+		L" \n"		
+		L"		placement \n"
+		L"		{ \n"
+		L"			clip: \"none\"; \n"
+		L"			margin: {t: 0; r: 0; b: 0; l: 0;}; \n"
+		L"			align: bottomcenter; \n"
+		L"			pos: \"auto\" \n"
+		L"			offset: {x: 0; y: 0;}; \n"
+		L"			angle: {x: 0; y: 0; z: 0;}; \n"
+		L"			org: \"auto\" \n"
+		L"			path: \"\"; \n"
+		L"		}; \n"
+		L" \n"
+		L"		font \n"
+		L"		{ \n"
+		L"			face: \"Arial\"; \n"
+		L"			size: 20; \n"
+		L"			weight: \"bold\"; \n"
+		L"			color: white; \n"
+		L"			underline: \"false\"; \n"
+		L"			strikethrough: \"false\"; \n"
+		L"			italic: \"false\"; \n"
+		L"			spacing: 0; \n"
+		L"			scale: {cx: 1; cy: 1;}; \n"
+		L"			kerning: \"true\"; \n"
+		L"		}; \n"
+		L" \n"
+		L"		background \n"
+		L"		{ \n"
+		L"			color: black; \n"
+		L"			size: 2; \n"
+		L"			type: \"outline\"; \n"
+		L"			blur: 0; \n"
+		L"		}; \n"
+		L" \n"
+		L"		shadow \n"
+		L"		{ \n"
+		L"			color: black {a: 128;}; \n"
+		L"			depth: 2; \n"
+		L"			angle: -45; \n"
+		L"			blur: 0; \n"
+		L"		}; \n"
+		L" \n"
+		L"		fill \n"
+		L"		{ \n"
+		L"			color: yellow; \n"
+		L"			width: 0; \n"
+		L"		}; \n"
+		L"	}; \n"
+		L"}; \n";
+}
diff --git a/vsfilter/libssf/SubtitleFile.h b/vsfilter/libssf/SubtitleFile.h
new file mode 100644
index 0000000000000000000000000000000000000000..1dc73836f0b2d71433eb7c3c1a60fc612bad64a9
--- /dev/null
+++ b/vsfilter/libssf/SubtitleFile.h
@@ -0,0 +1,73 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "File.h"
+#include "Subtitle.h"
+
+namespace ssf
+{
+	class SubtitleFile : public File
+	{
+		static LPCWSTR s_predef;
+
+	public:
+		struct SegmentItem
+		{
+			Definition* pDef;
+			float start, stop;
+		};
+
+		class Segment : public CAtlList<SegmentItem>
+		{
+		public:
+			float m_start, m_stop; 
+			Segment() {}
+			Segment(float start, float stop, const SegmentItem* si = NULL);
+			Segment(const Segment& s);
+			void operator = (const Segment& s);
+		};
+
+		class SegmentList : public CAtlList<Segment> 
+		{
+			CAtlArray<Segment*> m_index;
+			size_t Index(bool fForce = false);
+
+		public:
+			void RemoveAll();
+			void Insert(float start, float stop, Definition* pDef);
+			void Lookup(float at, CAtlList<SegmentItem>& sis);
+			bool Lookup(float at, size_t& k);
+			const Segment* GetSegment(size_t k);
+		};
+
+		SegmentList m_segments;
+
+	public:
+		SubtitleFile();
+		virtual ~SubtitleFile();
+
+		void Parse(InputStream& s);
+		void Append(InputStream& s, float start, float stop, bool fSetTime = false);
+		bool Lookup(float at, CAutoPtrList<Subtitle>& subs);
+	};
+}
\ No newline at end of file
diff --git a/vsfilter/libssf/demo/demo.ssa b/vsfilter/libssf/demo/demo.ssa
new file mode 100644
index 0000000000000000000000000000000000000000..8e3d18e3d16969ea4914ba9c970465a77fa94287
--- /dev/null
+++ b/vsfilter/libssf/demo/demo.ssa
@@ -0,0 +1,87 @@
+[Script Info]
+; This is a Sub Station Alpha v4 script.
+; For Sub Station Alpha info and downloads,
+; go to http://www.eswat.demon.co.uk/
+; or email kotus@eswat.demon.co.uk
+; 
+ScriptType: v4.00
+Collisions: Normal
+PlayResX: 320
+PlayResY: 240
+Timer: 100.0000
+
+[V4 Styles]
+Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding
+Style: MainB,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0,-1,1,2,4,1,16,16,16,0,0
+Style: Default,Arial,18,&Hffffff,&H00ffff,&H000000,&H000000, -1, 0,1,2,3,2,20,20,20,0,1
+Style: MainT,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,0
+Style: Karaoke,Arial,14,&H40ffff,&Hff4040,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,0
+Style: B4S4B,Arial,14,&He0e0e0,&H00ff00,&H804000,&H804000, 0,-1,1,4,4,5,16,16,40,0,0
+Style: B0S0K,Arial,14,&He0e0e0,&H00ff00,&H000000,&H000000, 0,-1,1,0,0,5,16,16,40,0,0
+Style: B2S2R,Arial,14,&He0e0e0,&H00ff00,&H004080,&H004080, 0,-1,1,2,2,5,16,16,40,0,0
+Style: B2S6G,Arial,14,&He0e0e0,&H00ff00,&H408000,&H408000, 0,-1,1,2,6,5,16,16,40,0,0
+Style: ShiftJIS,MS Gothic,20,&He0e0e0,&H00ff00,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,128
+Style: Timer,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0,-1,1,2,4,5,16,16,40,0,0
+
+
+[Events]
+Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
+Dialogue: Marked=0,0:00:00.00,0:00:05.00,MainB,,0000,0000,0000,!Effect,{\q1}Subtitler filter for VirtualDub\NDemonstration script\N\NAvery Lee <phaeron@virtualdub.org>
+Dialogue: Marked=0,0:00:05.00,0:00:10.00,MainB,,0000,0000,0000,!Effect,{\q1}Introduction\N\NThis script will demonstrate some of the simpler features of the subtitler. You will want to refer to the {\fnCourier New\b1}readme.txt{\r} file for a more complete reference.
+Dialogue: Marked=0,0:00:10.00,0:00:21.00,MainB,,0000,0000,0000,!Effect,{\q1}Dialogue lines\N\NScripts are based on dialogue lines. Each dialogue line has a start time, an end time, and text to display in between.
+Dialogue: Marked=0,0:00:10.00,0:00:11.00,Timer,,0000,0000,0000,!Effect,0:00
+Dialogue: Marked=0,0:00:11.00,0:00:12.00,Timer,,0000,0000,0000,!Effect,0:01
+Dialogue: Marked=0,0:00:12.00,0:00:13.00,Timer,,0000,0000,0000,!Effect,0:02
+Dialogue: Marked=0,0:00:13.00,0:00:14.00,Timer,,0000,0000,0000,!Effect,0:03
+Dialogue: Marked=0,0:00:14.00,0:00:15.00,Timer,,0000,0000,0000,!Effect,0:04
+Dialogue: Marked=0,0:00:15.00,0:00:16.00,Timer,,0000,0000,0000,!Effect,0:05
+Dialogue: Marked=0,0:00:16.00,0:00:17.00,Timer,,0000,0000,0000,!Effect,0:06
+Dialogue: Marked=0,0:00:17.00,0:00:18.00,Timer,,0000,0000,0000,!Effect,0:07
+Dialogue: Marked=0,0:00:18.00,0:00:19.00,Timer,,0000,0000,0000,!Effect,0:08
+Dialogue: Marked=0,0:00:19.00,0:00:20.00,Timer,,0000,0000,0000,!Effect,0:09
+Dialogue: Marked=0,0:00:20.00,0:00:21.00,Timer,,0000,0000,0000,!Effect,0:10
+Dialogue: Marked=0,0:00:12.00,0:00:15.00,MainT,,0000,0000,0000,!Effect,{\c&H80FF00&}Hello.
+Dialogue: Marked=0,0:00:16.00,0:00:19.00,MainT,,0000,0000,0000,!Effect,{\c&HE0E0E0&}Oh, hi.
+Dialogue: Marked=0,0:00:10.00,0:00:12.00,MainT,,0000,0000,0080,!Effect,{\c&H407F00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:12.00,0:00:15.00,MainT,,0000,0000,0080,!Effect,{\c&H80FF00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:15.00,0:00:21.00,MainT,,0000,0000,0080,!Effect,{\c&H407F00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:10.00,0:00:16.00,MainT,,0000,0000,0080,!Effect,{\c&H707070&}[06:00-09:00] Oh, hi.
+Dialogue: Marked=0,0:00:16.00,0:00:19.00,MainT,,0000,0000,0080,!Effect,{\c&HE0E0E0&}[06:00-09:00] Oh, hi.
+Dialogue: Marked=0,0:00:19.00,0:00:21.00,MainT,,0000,0000,0080,!Effect,{\c&H707070&}[06:00-09:00] Oh, hi.
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0055,0055,0000,!Effect,{\q1\a10}Alignment\N\NDialogue lines may be placed in any of three horizontal placements and any of three vertical placements.
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a1\c&HE0E0E0&}Bottom\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a2\c&HE0E0E0&}Bottom\Ncenter
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a3\c&HE0E0E0&}Bottom\Nright
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a5\c&HE0E0E0&}Top\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a6\c&HE0E0E0&}Top\Ncenter
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a7\c&HE0E0E0&}Top\Nright
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a9\c&HE0E0E0&}Middle\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a11\c&HE0E0E0&}Middle\Nright
+Dialogue: Marked=0,0:00:35.00,0:00:45.00,MainB,,0000,0000,0000,!Effect,{\q1}Text styles\N\NText may be varied in font, size, color, and style.
+Dialogue: Marked=0,0:00:35.00,0:00:45.00,MainT,,0000,0000,0000,!Effect,{\q1}You can have your text in {\c&H4060FF&}red{\r}, {\c&H60FF40&}green{\r}, and {\c&HFF6040&}blue{\r}; {\fnCourier New}Courier{\r}; {\fs16}16 point{\r} or {\fs32}32 point{\r}; {\b1}bold{\r}, {\i1}italic{\r}, or even {\b1\i1}bold italic{\r}.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainB,,0000,0000,0000,!Effect,{\q1}Wrapping styles\N\NThree wrapping modes are supported: manual only, automatic wrapping, and smart wrapping. Smart wrapping is automatic wrapping, but with the lines broken as evenly as possible.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0000,!Effect,{\q2\c&HE0FF80&}This line uses manual wrapping and won't\nbe wrapped except where explicitly broken.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0045,!Effect,{\q1\c&H80E0FF&}This line uses automatic wrapping and is broken automatically.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0090,!Effect,{\q0\a6\c&HFFE080&}This line uses smart wrapping and is broken with even lines. It's good for centered text.
+Dialogue: Marked=0,0:00:59.00,0:01:24.00,MainB,,0000,0000,0000,!Effect,{\q1}Collisions\N\NIf two dialogue items attempt to display at the same time and place, the second one is pushed clear of the first. However, text stays in place after it is resolved, even if the collider disappears.
+Dialogue: Marked=0,0:01:02.00,0:01:07.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Man, I gotta get out of here!
+Dialogue: Marked=0,0:01:07.00,0:01:17.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Which way should I go? I'm not going back to school....
+Dialogue: Marked=0,0:01:10.00,0:01:21.00,MainT,,0000,0000,0000,!Effect,{\q1\a7\c&H50D0FF&}Ranma, you're in the way! Move!
+Dialogue: Marked=0,0:01:15.00,0:01:21.30,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Why would I move for an uncute tomboy like you?
+Dialogue: Marked=0,0:01:21.00,0:01:25.00,MainT,,0000,0000,0000,!Effect,{\q1\a7\c&H50D0FF&}Ranma no baka!!!
+Dialogue: Marked=0,0:01:21.50,0:01:24.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}*wham*
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,MainB,,0000,0000,0000,!Effect,{\q1}Shadows and borders\N\NYou can vary the depth of the translucent shadow as well as the thickness and color of the border.
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B0S0K,,0000,0000,0000,!Effect,Border=0, shadow=0
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B2S2R,,0000,0000,0030,!Effect,Border=2, shadow=2 (red)
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B2S6G,,0000,0000,0060,!Effect,Border=2, shadow=6 (green)
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B4S4B,,0000,0000,0090,!Effect,Border=4, shadow=4 (blue)
+Dialogue: Marked=0,0:01:38.00,0:01:48.00,MainB,,0000,0000,0000,!Effect,{\q1}Karaoke\N\NLetting people sing to music is dangerous. But if they're going to do it anyway, they might as well have some help. Both snap and smooth styles are supported.
+Dialogue: Marked=0,0:01:40.00,0:01:45.00,Karaoke,,0000,0000,0000,Karaoke,{\fs18\k100}one {\k100}two {\k50}three-{\k50}and {\k100}four
+Dialogue: Marked=0,0:01:40.00,0:01:45.00,Karaoke,,0000,0000,0030,Karaoke,{\fs18\K100}one {\K100}two {\K50}three-{\K50}and {\K100}four
+Dialogue: Marked=0,0:01:50.00,0:02:00.00,MainB,,0000,0000,0000,!Effect,{\q1}DBCS (double-byte character set) support\N\NIf you have the appropriate language support installed, you can display far-east text in your subtitles. (By the way, I don't really know Japanese, so I'm kind of winged it above with the JWPce dictionary.)
+Dialogue: Marked=0,0:01:50.00,0:02:00.00,ShiftJIS,,0000,0000,0000,!Effect,{\q1}{\fe0}[Keitarou]{\fe128}‚¾BBB‘åä•vH
+Dialogue: Marked=0,0:01:52.00,0:02:00.00,ShiftJIS,,0000,0000,0000,!Effect,{\a7\q1\c&HFFFF00&\fe0}[Naru]{\fe128}‰½‚æI
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainB,,0000,0000,0000,!Effect,{\q1}Motion effects\N\NYou can scroll text in like a banner, or up the screen.
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainT,,0000,0000,0000,Banner;30,Text is fun.  We like scrolling text.
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainT,,0000,0000,0000,Scroll Up;40;50;120,{\q1}You can scroll lots of text up this way. Personally, I find it a little annoying to have a lot of text coming up very slowly on screen, but it's a useful technique to have for credits and other long lists in tabular form.
+Dialogue: Marked=0,0:02:19.00,0:02:50.00,MainB,,0000,0000,0000,!Effect,{\q1}Conclusion\N\NThat concludes the demo. I hope you've enjoyed the brief glimpse I've given you here, and encourage you to read the {\fnCourier New\b1}readme.txt{\r} file for more information on the effects you've seen here. Also, if you're bored, try loading this script into Kotus' Sub Station Alpha V4 program. Not all effects will render correctly, but most of the file will. Have fun, and keep on subtitling!\N\N{\fnCourier New}--{\r}Avery Lee <phaeron@virtualdub.org>\N{\fnCourier New}\h\h{\r}Author of VirtualDub and the 'subtitler' filter
diff --git a/vsfilter/libssf/demo/demo.ssf b/vsfilter/libssf/demo/demo.ssf
new file mode 100644
index 0000000000000000000000000000000000000000..2de374d380143db20658a112a4f20b5b744ab079
--- /dev/null
+++ b/vsfilter/libssf/demo/demo.ssf
@@ -0,0 +1,595 @@
+/*
+	This file is the direct translation of demo.ssa of the "subtitler" virtualdub plugin
+*/
+
+file#file
+{
+	format: "ssf";
+	version: 1;
+};
+
+/*
+PlayResX: 320
+PlayResY: 240
+*/
+
+subtitle#subtitle
+{
+	frame
+	{
+		reference: "video"; 
+		resolution: {cx: 320; cy: 240;};
+	};
+	
+	style
+	{
+		placement {align: bottomleft; margin {t: 16; r: 16; b: 16; l: 16;};};
+		font {size: 14; weight: "normal"; color: yellow;};
+		shadow.depth: 4;
+	};
+};
+
+/*
+Style: MainB,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0,-1,1,2,4,1,16,16,16,0,0
+Style: Default,Arial,18,&Hffffff,&H00ffff,&H000000,&H000000, -1, 0,1,2,3,2,20,20,20,0,1
+Style: MainT,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,0
+Style: Karaoke,Arial,14,&H40ffff,&Hff4040,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,0
+Style: B4S4B,Arial,14,&He0e0e0,&H00ff00,&H804000,&H804000, 0,-1,1,4,4,5,16,16,40,0,0
+Style: B0S0K,Arial,14,&He0e0e0,&H00ff00,&H000000,&H000000, 0,-1,1,0,0,5,16,16,40,0,0
+Style: B2S2R,Arial,14,&He0e0e0,&H00ff00,&H004080,&H004080, 0,-1,1,2,2,5,16,16,40,0,0
+Style: B2S6G,Arial,14,&He0e0e0,&H00ff00,&H408000,&H408000, 0,-1,1,2,6,5,16,16,40,0,0
+Style: ShiftJIS,MS Gothic,20,&He0e0e0,&H00ff00,&H000000,&H000000, 0, 0,1,2,4,5,16,16,16,0,128
+Style: Timer,Arial,14,&H00ffff,&H00ff00,&H000000,&H000000, 0,-1,1,2,4,5,16,16,40,0,0
+*/
+
+#MainT {placement.align: topleft;};
+#MainB {font.italic: "true";};
+#Karaoke : MainT {font.color.b: 0x40;};
+#Timer : MainT MainB {placement.margin.t: 40;};
+#BxSxC : Timer {font.color {r: 0xe0; g: 0xe0; b: 0xe0;};};
+#B4S4B : BxSxC {background {color: {r: 0x00; g: 0x40; b: 0x80;}; size: 4;};};
+#B0S0K : BxSxC {background.size: 0; shadow.depth: 0;};
+#B2S2R : BxSxC {background.color {r: 0x80; g: 0x40; b: 0x00;}; shadow.depth: 2;};
+#B2S6G : BxSxC {background.color {r: 0x00; g: 0x80; b: 0x40;}; shadow.depth: 6;};
+#ShiftJIS : MainT {font {face: "MS Gothic"; size: 20; color: {r: 0xe0; g: 0xe0; b: 0xe0;};};};
+
+/*
+Dialogue: Marked=0,0:00:00.00,0:00:05.00,MainB,,0000,0000,0000,!Effect,{\q1}Subtitler filter for VirtualDub\NDemonstration script\N\NAvery Lee <phaeron@virtualdub.org>
+Dialogue: Marked=0,0:00:05.00,0:00:10.00,MainB,,0000,0000,0000,!Effect,{\q1}Introduction\N\NThis script will demonstrate some of the simpler features of the subtitler. You will want to refer to the {\fnCourier New\b1}readme.txt{\r} file for a more complete reference.
+Dialogue: Marked=0,0:00:10.00,0:00:21.00,MainB,,0000,0000,0000,!Effect,{\q1}Dialogue lines\N\NScripts are based on dialogue lines. Each dialogue line has a start time, an end time, and text to display in between.
+*/
+
+#notes
+{
+	style: MainB;
+};
+
+subtitle : notes
+{
+	time {start: 00:00:00.000; stop: 00:00:05.000;};
+	
+	@ { 
+		Subtitler filter for VirtualDub\n
+		Demonstration script\n\n
+		Avery Lee <phaeron@virtualdub.org>
+	};
+};
+
+subtitle : notes
+{
+	time {start: 00:00:05.000; stop: 00:00:10.000;};
+	
+	@ {
+		Introduction\n\n
+		This script will demonstrate some of the simpler features of the subtitler. You will want to 
+		refer to the [{font {face: "Courier New"; weight: "bold";};}] {readme.txt} file for a more 
+		complete reference.
+	};
+};
+
+subtitle : notes
+{
+	time {start: 00:00:10.000; stop: 00:00:21.000;};
+
+	@ {
+		Dialogue lines\n\n
+		Scripts are based on dialogue lines. Each dialogue line has a start time, an end time, and text 
+		to display in between.
+	};
+};
+
+/*
+Dialogue: Marked=0,0:00:10.00,0:00:11.00,Timer,,0000,0000,0000,!Effect,0:00
+Dialogue: Marked=0,0:00:11.00,0:00:12.00,Timer,,0000,0000,0000,!Effect,0:01
+Dialogue: Marked=0,0:00:12.00,0:00:13.00,Timer,,0000,0000,0000,!Effect,0:02
+Dialogue: Marked=0,0:00:13.00,0:00:14.00,Timer,,0000,0000,0000,!Effect,0:03
+Dialogue: Marked=0,0:00:14.00,0:00:15.00,Timer,,0000,0000,0000,!Effect,0:04
+Dialogue: Marked=0,0:00:15.00,0:00:16.00,Timer,,0000,0000,0000,!Effect,0:05
+Dialogue: Marked=0,0:00:16.00,0:00:17.00,Timer,,0000,0000,0000,!Effect,0:06
+Dialogue: Marked=0,0:00:17.00,0:00:18.00,Timer,,0000,0000,0000,!Effect,0:07
+Dialogue: Marked=0,0:00:18.00,0:00:19.00,Timer,,0000,0000,0000,!Effect,0:08
+Dialogue: Marked=0,0:00:19.00,0:00:20.00,Timer,,0000,0000,0000,!Effect,0:09
+Dialogue: Marked=0,0:00:20.00,0:00:21.00,Timer,,0000,0000,0000,!Effect,0:10
+*/
+
+#clocktest
+{
+	time {start: +0s; stop: +1s;};
+	style: Timer;
+};
+
+subtitle : clocktest {@ {0:00}; time.start: 00:00:10.000;};
+subtitle : clocktest {@ {0:01};};
+subtitle : clocktest {@ {0:02};};
+subtitle : clocktest {@ {0:03};};
+subtitle : clocktest {@ {0:04};};
+subtitle : clocktest {@ {0:05};};
+subtitle : clocktest {@ {0:06};};
+subtitle : clocktest {@ {0:07};};
+subtitle : clocktest {@ {0:08};};
+subtitle : clocktest {@ {0:09};};
+subtitle : clocktest {@ {0:10};};
+
+/*
+Dialogue: Marked=0,0:00:12.00,0:00:15.00,MainT,,0000,0000,0000,!Effect,{\c&H80FF00&}Hello.
+Dialogue: Marked=0,0:00:16.00,0:00:19.00,MainT,,0000,0000,0000,!Effect,{\c&HE0E0E0&}Oh, hi.
+Dialogue: Marked=0,0:00:10.00,0:00:12.00,MainT,,0000,0000,0080,!Effect,{\c&H407F00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:12.00,0:00:15.00,MainT,,0000,0000,0080,!Effect,{\c&H80FF00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:15.00,0:00:21.00,MainT,,0000,0000,0080,!Effect,{\c&H407F00&}[02:00-05:00] Hello.
+Dialogue: Marked=0,0:00:10.00,0:00:16.00,MainT,,0000,0000,0080,!Effect,{\c&H707070&}[06:00-09:00] Oh, hi.
+Dialogue: Marked=0,0:00:16.00,0:00:19.00,MainT,,0000,0000,0080,!Effect,{\c&HE0E0E0&}[06:00-09:00] Oh, hi.
+Dialogue: Marked=0,0:00:19.00,0:00:21.00,MainT,,0000,0000,0080,!Effect,{\c&H707070&}[06:00-09:00] Oh, hi.
+*/
+
+subtitle
+{
+	time {start: 00:00:12.000; stop: 00:00:15.000;};
+	style: MainT {font.color {r: 0x00; g: 0xff; b: 0x80;};};
+	@ {Hello.};
+};
+
+subtitle
+{
+	time {start: 00:00:16.000; stop: 00:00:19.000;};
+	style: MainT {font.color {r: 0xe0; g: 0xe0; b: 0xe0;};};
+	@ {Oh, hi.};
+};
+
+#dialogtest
+{
+	style: MainT {placement.margin.t: 80;};
+	time.start: +0s;
+};
+
+#dialogtest1 : dialogtest
+{
+	@ {\[02:00-05:00\] Hello.};
+};
+
+#dialogtest2 : dialogtest
+{
+	@ {\[06:00-09:00\] Oh, hi.};
+};
+
+subtitle : dialogtest1
+{
+	time.start: 00:00:10.000;
+	time.stop: +2s;
+	style.font.color {r: 0x00; g: 0x7f; b: 0x40;};
+};
+
+subtitle : dialogtest1
+{
+	time.stop: +3s;
+	style.font.color {r: 0x00; g: 0xff; b: 0x80;};
+};
+
+subtitle : dialogtest1
+{
+	time.stop: +6s;
+	style.font.color {r: 0x00; g: 0x7f; b: 0x40;};
+};
+
+subtitle : dialogtest2
+{
+	time.start: 00:00:10.000;
+	time.stop: +6s;
+	style.font.color {r: 0x70; g: 0x70; b: 0x70;};
+};
+
+subtitle : dialogtest2
+{
+	time.stop: +3s;
+	style.font.color {r: 0xe0; g: 0xe0; b: 0xe0;};
+};
+
+subtitle : dialogtest2
+{
+	time.stop: +2s;
+	style.font.color {r: 0x70; g: 0x70; b: 0x70;};
+};
+
+/*
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0055,0055,0000,!Effect,{\q1\a10}Alignment\N\NDialogue lines may be placed in any of three horizontal placements and any of three vertical placements.
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a1\c&HE0E0E0&}Bottom\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a2\c&HE0E0E0&}Bottom\Ncenter
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a3\c&HE0E0E0&}Bottom\Nright
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a5\c&HE0E0E0&}Top\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a6\c&HE0E0E0&}Top\Ncenter
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a7\c&HE0E0E0&}Top\Nright
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a9\c&HE0E0E0&}Middle\Nleft
+Dialogue: Marked=0,0:00:23.00,0:00:33.00,MainB,,0008,0008,0008,!Effect,{\a11\c&HE0E0E0&}Middle\Nright
+*/
+
+#aligntest : notes
+{
+	time {start: 00:00:23.000; stop: 00:00:33.000;};
+	style {placement.margin {t: 8; r: 8; b: 8; l: 8;};};
+};
+
+#aligntestside : aligntest
+{
+	style.font.color {r: 0xe0; g: 0xe0; b: 0xe0;};
+};
+
+subtitle : aligntest
+{
+	style.placement {align: middlecenter; margin {r: 55; l: 55;};};
+	
+	@ {
+		Alignment\n\n
+		Dialogue lines may be placed in any of three horizontal placements and any of three vertical placements.
+	};
+};
+
+subtitle : aligntestside {style.placement.align: bottomleft;   @ {Bottom\nleft};};
+subtitle : aligntestside {style.placement.align: bottomcenter; @ {Bottom\ncenter};};
+subtitle : aligntestside {style.placement.align: bottomright;  @ {Bottom\nright};};
+subtitle : aligntestside {style.placement.align: topleft;      @ {Top\nleft};};
+subtitle : aligntestside {style.placement.align: topcenter;    @ {Top\ncenter};};
+subtitle : aligntestside {style.placement.align: topright;     @ {Top\nright};};
+subtitle : aligntestside {style.placement.align: middleleft;   @ {Middle\nleft};};
+subtitle : aligntestside {style.placement.align: middleright;  @ {Middle\nright};};
+
+/*
+Dialogue: Marked=0,0:00:35.00,0:00:45.00,MainB,,0000,0000,0000,!Effect,{\q1}Text styles\N\NText may be varied in font, size, color, and style.
+Dialogue: Marked=0,0:00:35.00,0:00:45.00,MainT,,0000,0000,0000,!Effect,{\q1}You can have your text in {\c&H4060FF&}red{\r}, {\c&H60FF40&}green{\r}, and {\c&HFF6040&}blue{\r}; {\fnCourier New}Courier{\r}; {\fs16}16 point{\r} or {\fs32}32 point{\r}; {\b1}bold{\r}, {\i1}italic{\r}, or even {\b1\i1}bold italic{\r}.
+*/
+
+#fonttest
+{
+	time {start: 00:00:35.000; stop: 00:00:45.000;};
+};
+
+subtitle : fonttest notes
+{
+	@ {
+		Text styles\n\n
+		Text may be varied in font, size, color, and style.
+	};
+};
+
+subtitle : fonttest
+{
+	style: MainT;
+	
+	@ {
+		You can have your text in 
+		[{font.color {r: 0xff; g: 0x60; b: 0x40;};}] {red}, 
+		[{font.color {r: 0x40; g: 0xff; b: 0x60;};}] {green}, and 
+		[{font.color {r: 0x40; g: 0x60; b: 0xff;};}] {blue};
+		[{font.face: "Courier New";}] {Courier};
+		[{font.size: 16;}] {16 point} or
+		[{font.size: 32;}] {32 point};
+		[{font.weight: "bold";}] {bold},
+		[{font.italic: "true";}] {italic},
+		or even 
+		[{font {weight: "bold"; italic: "true";};}] {bold italic}.
+	};
+};
+
+/*
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainB,,0000,0000,0000,!Effect,{\q1}Wrapping styles\N\NThree wrapping modes are supported: manual only, automatic wrapping, and smart wrapping. Smart wrapping is automatic wrapping, but with the lines broken as evenly as possible.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0000,!Effect,{\q2\c&HE0FF80&}This line uses manual wrapping and won't\nbe wrapped except where explicitly broken.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0045,!Effect,{\q1\c&H80E0FF&}This line uses automatic wrapping and is broken automatically.
+Dialogue: Marked=0,0:00:47.00,0:00:57.00,MainT,,0000,0000,0090,!Effect,{\q0\a6\c&HFFE080&}This line uses smart wrapping and is broken with even lines. It's good for centered text.
+*/
+
+#wraptest
+{
+	time {start: 00:00:47.000; stop: 00:00:57.000;};
+};
+
+subtitle : wraptest notes
+{
+	@ {
+		Wrapping styles\n\n
+		Three wrapping modes are supported: manual only, automatic wrapping, and smart wrapping. 
+		Smart wrapping is automatic wrapping, but with the lines broken as evenly as possible.
+	};
+};
+
+subtitle : wraptest
+{
+	style: MainT {font.color {r: 0x80; g: 0xff; b: 0xe0;};};
+	wrap: "manual";
+	@ {This line uses manual wrapping and won't\nbe wrapped except where explicitly broken.};
+};
+
+subtitle : wraptest
+{
+	style: MainT {font.color {r: 0xff; g: 0xe0; b: 0x80;}; placement.margin.t: 45;};
+	wrap: "normal"; 
+	@ {This line uses automatic wrapping and is broken automatically.};
+};
+
+subtitle : wraptest
+{
+	style: MainT {font.color {r: 0x80; g: 0xe0; b: 0xff;}; placement.margin.t: 90; placement.align: topcenter; };
+	wrap: "even"; 
+	@ {This line uses smart wrapping and is broken with even lines. It's good for centered text.};
+};
+
+/*
+Dialogue: Marked=0,0:00:59.00,0:01:24.00,MainB,,0000,0000,0000,!Effect,{\q1}Collisions\N\NIf two dialogue items attempt to display at the same time and place, the second one is pushed clear of the first. However, text stays in place after it is resolved, even if the collider disappears.
+Dialogue: Marked=0,0:01:02.00,0:01:07.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Man, I gotta get out of here!
+Dialogue: Marked=0,0:01:07.00,0:01:17.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Which way should I go? I'm not going back to school....
+Dialogue: Marked=0,0:01:10.00,0:01:21.00,MainT,,0000,0000,0000,!Effect,{\q1\a7\c&H50D0FF&}Ranma, you're in the way! Move!
+Dialogue: Marked=0,0:01:15.00,0:01:21.30,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}Why would I move for an uncute tomboy like you?
+Dialogue: Marked=0,0:01:21.00,0:01:25.00,MainT,,0000,0000,0000,!Effect,{\q1\a7\c&H50D0FF&}Ranma no baka!!!
+Dialogue: Marked=0,0:01:21.50,0:01:24.00,MainT,,0000,0000,0000,!Effect,{\q1\c&HE0E0E0&}*wham*
+*/
+
+subtitle : notes
+{
+	time {start: 00:00:59.000; stop: 00:01:24.000;};
+	
+	@ {
+		Collisions\n\n
+		If two dialogue items attempt to display at the same time and place, the second one is 
+		pushed clear of the first. However, text stays in place after it is resolved, even if 
+		the collider disappears.
+	};
+};
+
+#actor1
+{
+	style: MainT {font.color {r: 0xe0; g: 0xe0; b: 0xe0;};};
+};
+
+#actor2
+{
+	style: MainT {font.color {r: 0xff; g: 0xd0; b: 0x50;}; placement.align: topright;};
+};
+
+subtitle : actor1
+{
+	time {start: 00:01:02.000; stop: 00:01:07.000;};
+	@ {Man, I gotta get out of here!};
+};
+
+subtitle : actor1
+{
+	time {start: 00:01:07.000; stop: 00:01:17.000;};
+	@ {Which way should I go? I'm not going back to school....};
+};
+
+subtitle : actor2
+{
+	time {start: 00:01:10.000; stop: 00:01:21.000;};
+	@ {Ranma, you're in the way! Move!};
+};
+
+subtitle : actor1
+{
+	time {start: 00:01:15.000; stop: 00:01:21.300;};
+	@ {Why would I move for an uncute tomboy like you?};
+};
+
+subtitle : actor2
+{
+	time {start: 00:01:21.000; stop: 00:01:25.000;};
+	@ {Ranma no baka!!!};
+};
+
+subtitle : actor1
+{
+	time {start: 00:01:21.500; stop: 00:01:24.000;};
+	@ {*wham*};
+};
+
+/*
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,MainB,,0000,0000,0000,!Effect,{\q1}Shadows and borders\N\NYou can vary the depth of the translucent shadow as well as the thickness and color of the border.
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B0S0K,,0000,0000,0000,!Effect,Border=0, shadow=0
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B2S2R,,0000,0000,0030,!Effect,Border=2, shadow=2 (red)
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B2S6G,,0000,0000,0060,!Effect,Border=2, shadow=6 (green)
+Dialogue: Marked=0,0:01:26.00,0:01:36.00,B4S4B,,0000,0000,0090,!Effect,Border=4, shadow=4 (blue)
+*/
+
+#bordershadowtest
+{
+	time {start: 00:01:26.000; stop: 00:01:36.000;};
+};
+
+subtitle : bordershadowtest notes
+{
+	@ {
+		Shadows and borders\n\n
+		You can vary the depth of the translucent shadow as well as the thickness and color of the border.
+	};
+};
+
+subtitle : bordershadowtest
+{
+	style: B0S0K;
+	@ {Border=0, shadow=0};
+};
+
+subtitle : bordershadowtest
+{
+	style: B2S2R {placement.margin.t: 30;};
+	@ {Border=2, shadow=2 (red)};
+};
+
+subtitle : bordershadowtest
+{
+	style: B2S6G {placement.margin.t: 60;};
+	@ {Border=2, shadow=6 (green)};
+};
+
+subtitle : bordershadowtest
+{
+	style: B4S4B {placement.margin.t: 90;};
+	@ {Border=4, shadow=4 (blue)};
+};
+
+/*
+Dialogue: Marked=0,0:01:38.00,0:01:48.00,MainB,,0000,0000,0000,!Effect,{\q1}Karaoke\N\NLetting people sing to music is dangerous. But if they're going to do it anyway, they might as well have some help. Both snap and smooth styles are supported.
+Dialogue: Marked=0,0:01:40.00,0:01:45.00,Karaoke,,0000,0000,0000,Karaoke,{\fs18\k100}one {\k100}two {\k50}three-{\k50}and {\k100}four
+Dialogue: Marked=0,0:01:40.00,0:01:45.00,Karaoke,,0000,0000,0030,Karaoke,{\fs18\K100}one {\K100}two {\K50}three-{\K50}and {\K100}four
+*/
+
+subtitle : notes
+{
+	time {start: 00:01:38.000; stop: 00:01:48.000;};
+
+	@ {
+		Karaoke\n\n
+		Letting people sing to music is dangerous. But if they're going to do it anyway, 
+		they might as well have some help. Both snap and smooth styles are supported.
+	};
+};
+
+#karaoketest
+{
+	time {start: 00:01:40.000; stop: 00:01:45.000;};
+	style: Karaoke {font.size: 18;};
+};
+
+#hlcolor {r: 0x40; g: 0x40; b: 0xff;};
+#hleffect1 {time {scale: 0.01; start: +0s;}; transition: "start"; font.color: hlcolor;};
+#hleffect2 {time {scale: 0.01; start: +0s;}; fill.width: 1;};
+
+subtitle : karaoketest
+{
+	@ {
+		[hleffect1 {time.stop: +100;}] {one}
+		[hleffect1 {time.stop: +100;}] {two}
+		[hleffect1 {time.stop:  +50;}] {three-}
+		[hleffect1 {time.stop:  +50;}] {and}
+		[hleffect1 {time.stop: +100;}] {four}
+	};
+};
+
+subtitle : karaoketest
+{
+	style {placement.margin.t: 30; fill.color: hlcolor;};
+
+	@ {
+		[hleffect2 {time.stop: +100;}] {one}
+		[hleffect2 {time.stop: +100;}] {two}
+		[hleffect2 {time.stop:  +50;}] {three-}
+		[hleffect2 {time.stop:  +50;}] {and}
+		[hleffect2 {time.stop: +100;}] {four}
+	};
+};
+
+/*
+Dialogue: Marked=0,0:01:50.00,0:02:00.00,MainB,,0000,0000,0000,!Effect,{\q1}DBCS (double-byte character set) support\N\NIf you have the appropriate language support installed, you can display far-east text in your subtitles. (By the way, I don't really know Japanese, so I'm kind of winged it above with the JWPce dictionary.)
+Dialogue: Marked=0,0:01:50.00,0:02:00.00,ShiftJIS,,0000,0000,0000,!Effect,{\q1}{\fe0}[Keitarou]{\fe128} ...
+Dialogue: Marked=0,0:01:52.00,0:02:00.00,ShiftJIS,,0000,0000,0000,!Effect,{\a7\q1\c&HFFFF00&\fe0}[Naru]{\fe128} ...
+*/
+
+subtitle : notes
+{
+	time {start: 00:01:50.000; stop: 00:02:00.000;};
+
+	@ {
+		DBCS (double-byte character set) support\n\n
+		If you have the appropriate language support installed, you can display far-east text 
+		in your subtitles. (By the way, I don't really know Japanese, so I'm kind of winged it 
+		above with the JWPce dictionary.)
+	};
+};
+
+subtitle
+{
+	time {start: 00:01:50.000; stop: 00:02:00.000;};
+	style: ShiftJIS;
+	@ {\[Keitarou\]だ。。。大丈夫?};
+};
+
+subtitle
+{
+	time {start: 00:01:52.000; stop: 00:02:00.000;};
+	style: ShiftJIS {font.color: cyan; placement.align: topright;};
+	@ {\[Naru\]何よ!};
+};
+
+/*
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainB,,0000,0000,0000,!Effect,{\q1}Motion effects\N\NYou can scroll text in like a banner, or up the screen.
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainT,,0000,0000,0000,Banner;30,Text is fun.  We like scrolling text.
+Dialogue: Marked=0,0:02:02.00,0:02:17.00,MainT,,0000,0000,0000,Scroll Up;40;50;120,{\q1}You can scroll lots of text up this way. Personally, I find it a little annoying to have a lot of text coming up very slowly on screen, but it's a useful technique to have for credits and other long lists in tabular form.
+*/
+
+#effecttest
+{
+	time {start: 00:02:02.000; stop: 00:02:17.000;};
+	style.placement.clip: "frame";
+};
+
+subtitle : effecttest notes
+{
+	@ {
+		Motion effects\n\n
+		You can scroll text in like a banner, or up the screen.
+	};
+};
+
+subtitle : effecttest
+{
+	wrap: "manual";
+	style: MainT {placement {margin {l: "right"; r: "left";}; align.h: "left";};};
+	#hscroll {time {start: "start"; stop: "stop";}; placement.align.h: "right";};
+
+	@ {
+		[hscroll]
+		Text is fun. We like scrolling text.
+	};
+};
+
+subtitle : effecttest
+{
+	style: MainT {placement {margin {t: 120; b: 200;}; clip {t: 40; b: 120;}; align.v: "top";};};
+	#vscroll {time {start: "start"; stop: "stop";}; placement.align.v: "bottom";};
+
+	@ {
+		[vscroll]
+		You can scroll lots of text up this way. Personally, I find it a little annoying 
+		to have a lot of text coming up very slowly on screen, but it's a useful technique 
+		to have for credits and other long lists in tabular form.
+	};
+};
+
+/*
+Dialogue: Marked=0,0:02:19.00,0:02:50.00,MainB,,0000,0000,0000,!Effect,{\q1}Conclusion\N\NThat concludes the demo. I hope you've enjoyed the brief glimpse I've given you here, and encourage you to read the {\fnCourier New\b1}readme.txt{\r} file for more information on the effects you've seen here. Also, if you're bored, try loading this script into Kotus' Sub Station Alpha V4 program. Not all effects will render correctly, but most of the file will. Have fun, and keep on subtitling!\N\N{\fnCourier New}--{\r}Avery Lee <phaeron@virtualdub.org>\N{\fnCourier New}\h\h{\r}Author of VirtualDub and the 'subtitler' filter
+*/
+
+subtitle : notes
+{
+	time {start: 00:02:19.000; stop: 00:02:50.000;};
+	
+	#monospace {font.face: "Courier New";};
+	#monospacebold : monospace {font.weight: "bold";};
+	
+	@ {
+		Conclusion\n\n
+		That concludes the demo. I hope you've enjoyed the brief glimpse I've given you here, 
+		and encourage you to read the [monospacebold] {readme.txt} file for more information 
+		on the effects you've seen here. Also, if you're bored, try loading this script into 
+		Kotus' Sub Station Alpha V4 program. Not all effects will render correctly, but most 
+		of the file will. Have fun, and keep on subtitling!\n\n
+		[monospace]{--}Avery Lee <phaeron@virtualdub.org>\n
+		[monospace]{\h\h}Author of VirtualDub and the 'subtitler' filter
+	};
+};
diff --git a/vsfilter/libssf/docs/ssf-specs.txt b/vsfilter/libssf/docs/ssf-specs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9ea8e8e6568aeb66b82511beccaef25fceff197d
--- /dev/null
+++ b/vsfilter/libssf/docs/ssf-specs.txt
@@ -0,0 +1,613 @@
+Structured Subtitle Format 1.0 (boring name, need a better one)
+------------------------------
+
+The encoding must be utf-8/16le/16be with the BOM at the beginning. 
+
+Parsing is prefered to be stream oriented, which means: 
+- new-line characters in text do not differ from the rest of the white-space characters
+- forward references are not allowed 
+
+Comments
+--------
+
+// a comment, ends at the end of the line
+/* this is a comment, too */
+
+Syntax
+------
+
+Fortunatelly, there is only one general way to define something. The elements are broken 
+into lines for better understanding, but they can be compressed into one single line as well, 
+whichever suits your preference. The term "type" could be read as "attribute" or "member of 
+a structure" too. 
+
+[!]
+[type[.type[..]]]
+[#name]
+[: | =]
+[refs | quoted string | num | bool | raw]
+;
+
+As you can see nearly everything is optional, even the terminating semi-colon is not required 
+when a closing bracket ends the definition anyway. However, either type or #name must be given,
+otherwise there would be no meaning of the expression.
+
+"!":
+- gives the definition high priority, normal priority types of the same kind cannot override it:
+	#a {!t: 123;};
+	#b {t: 234;};
+	#c a b; // t of c equals to 123
+	- works on references too:
+	!#a {t: 123;};
+	#b {t: 234;};
+	#ab a b;
+	#c ab; // t of c still equals to 123
+
+"type" and "name":
+- alphanumeric or underscore characters only, without spaces
+- cannot start with a number
+- case-sensitive
+
+"type":
+- type is optional but can be inherited through referencing other names which happen to have a type already
+	color#c1 {a: 0x80;};
+	#c2: c1; // c2 also has the type of color now
+	#c3: c2; // c3 too
+- if there is a type already additional references cannot change it
+- when mixing different types (not recommended) the first one decides
+- there is one special type which have an important but a limited use: @ (see 'subtitle' for an example usage)
+	- it is not parsed for references or nested definitions
+	- the content of the following block {...} is saved as-is for the application
+	- cannot be global or named and therefore referenced
+	- { and } have to be escaped with \ inside the block (other block specific characters may as well)
+- type.type.type.... is equal to writing: type {type {type {...};};};
+
+"name":
+- every name is globally visible
+- redefining a name is forbidden, unless it was predefined by the application
+- using the type as the name (type#type) can change the type's default values
+	- however, the scope is important:
+	
+	subtitle#subtitle {style.font.size: 20;};
+	style#style {font.size: 30;};
+	style#s1 {font.face: "Arial";};
+	style#s2 : s1 {font.color: red;};
+	subtitle#a {style: s2 {font.weight: "normal";};}; 
+
+	Here font.size equals to 20, because it inherits from subtitle#subtitle.style instead of the 
+	global style#style, additionally it gets the properties of s2-s1 and the inline def. If it also 
+	inherited properties from style#style through s2-s1 indirectly, there would be two default base 
+	definitions and the order of overriding eachother would not be clear.
+	
+	subtitle#a.style
+		<- subtitle#subtitle.style 
+		<- s2 <- s1 <-NOT- style#style
+		<- {font.weight: "normal";}
+
+"refs":
+- combination of any names or nested definitions separated by spaces
+- forward references are not allowed
+- referencing a value type directly is not allowed: (TODO: this may change)
+	OK:
+	color#c1: {a: 12;};
+	color#c2: c1;
+	BAD:
+	#twelve: 12;
+	color#c2: {a: twelve;};
+- 'name' must have been defined by a parent node
+	OK:
+	#c1: {a: 12;};
+	style#s1 {color: c1;};
+	style#s2 {color: c1;};
+	BAD:
+	style#s1 {color#c1: {a: 12;};};
+	style#s2 {color: c1;}; // c1 cannot be accessed here
+
+"quoted string" or 'quoted string':
+- \ escapes characters, including " and '
+- cannot contain any new-line characters
+
+"num":
+- decimal, hexadecimal (prefixed with: 0x), float [+ unit (optional, see 'time' below for an example)]
+- there are numbers with restricted range or special formatting:
+	- degrees: <num> mod 360
+	- percent: must be between 0 and 1
+	- time: [+] [<num>:[<num>:[<num>.]]]<num> | <num>h | <num>m | <num>s | <num>ms
+
+"bool":
+- "true" | "false" | "on" | "off" | "yes" | "no" | 1 | 0
+- unrecognizable values will result in unpredictable behaviour, since there can't be a default fallback value
+
+Recognized types and attributes
+-------------------------------
+
+file
+{
+	format: <string>; // identifies the file format ("ssf")
+	version: <num>; // file format version (1)
+	language: <string>; // iso6392
+	title: <string>;
+	year: <num>;
+	author: <string>;
+};
+
+color
+{
+	// a, r, g, b: 0 - 255 or 0x00 - 0xff
+
+	a: <num>;
+	r: <num>;
+	g: <num>;
+	b: <num>;
+};
+
+point
+{
+	x: <num>;
+	y: <num>;
+};
+
+size
+{
+	cx: <num>;
+	cy: <num>;
+};
+
+rect
+{
+	t: <num>;
+	r: <num>;
+	b: <num>;
+	l: <num>;
+};
+
+align
+{
+	// when given in percent, 0 means top/left, 1 bottom/right, 0.5 middle/center
+	
+	v: ["top" | "middle" | "bottom" | <percent>];
+	h: ["left" | "center" | "right" | <percent>];
+};
+
+angle
+{
+	x: <degrees>;
+	y: <degrees>;
+	z: <degrees>;
+};
+
+frame
+{
+	reference: ["video" | "window"];
+	resolution: <size>;
+};
+
+direction
+{
+	primary: ["right" | "left" | "down" | "up"];
+	secondary: ["right" | "left" | "down" | "up"]; // must be perpendicular to primary
+};
+
+placement
+{
+	clip: ["none" | "frame" | <rect>]; // anything drawn outside this rectangle is clipped, negative or larger than 'resolution' values are not allowed for rect
+	margin: <rect>; // "top" "right" "bottom" "left" are also valid values for the rect members, they refer to the values of the "frame" rect (0, 0, frame.resolution.cx, frame.resolution.cy)
+	align: <align>;
+	pos: ["auto" | <point>]; // absolute values, pos.x or pos.y can be animated only when both the source and destination style defined it
+	offset: <point>; // relative to pos, unlike pos this can be applied to fragments of the text as an override
+	angle: <angle>; // origin of rotation is the alignment point, unless it is overriden
+	org: ["auto" | <point>]; // override for the origin
+	path: <string>; // a series of x y coord pairs (at least two points)
+};
+
+font
+{
+	face: <string>;
+	size: <num>;
+	weight: ["normal" | "bold" | "thin" | <num>];
+	color: <color>;
+	underline: <bool>;
+	strikethrough: <bool>;
+	italic: <bool>;
+	spacing: <num>;
+	scale: <size>;
+	kerning: <bool>;
+};
+
+background
+{
+	color: <color>;
+	size: <num>;
+	type: ["outline" | "enlarge" | "box"];
+	blur: <num>; // number of passes
+
+	// "enlarge" can be computed faster, but because it follows the path of the original outline it is 
+	// not rounded and size > 1 can make it look pretty bad if it intersects with itself.
+};
+
+shadow
+{
+	color: <color>;
+	depth: <num>;
+	angle: <degrees>;
+	blur: <num>; // number of passes
+};
+
+fill
+{
+	color: <color>;
+	width: <percent>;
+	
+	// It cannot be applied to the same text multiple times as an override.
+	//
+	// #k1 {fill.width:1; time {start: +0; stop: +1s;}};
+	// #k2 {fill.width:1; time {start: 0; stop: 2s;}};
+	//
+	// OK: 
+	// [k1] {Hello} 
+	//
+	// BAD: 
+	// [k1] {Wo[k2]{r}ld!}
+	//
+};
+
+time
+{
+	id: [<string> | <num>];
+	start: <time>; // inclusive
+	stop: <time>; // exclusive
+	scale: <num>; // if the time was set WITHOUT a unit, then start and stop are measured in [scale * seconds]
+};
+
+style
+{
+	linebreak: ["word" | "char" | "none"]; // ignored if subtitle.wrap: "manual"
+	placement: <placement>;
+	font: <font>;
+	background: <background>;
+	shadow: <shadow>;
+	fill: <fill>;
+};
+
+animation
+{
+	time: <time>; // before start: previous style, after stop: this style, animation is done between start and stop according to transition
+	transition: ["linear" | "start" | "stop" | <num>]; // <num> is the same as the acceleration parameter of advanced ssa (see the "ass-specs"), "start" or "stop" sets num to 0 and inf, "linear" sets it to 1.0
+	loop: <num>; // the number of times the effect should repeat, e.g. loop set to 2 makes the style turn from src to dst twice, between start => (start+stop)/2 and (start+stop)/2 => stop 
+	direction: ["fw" | "bw" | "fwbw" | "bwfw"]; // "bwfw" and "fwbw" makes the value of loop multiplied by two internally
+};
+
+subtitle
+{
+	frame: <frame>;
+	direction: <direction>;
+	wrap: ["normal" | "even" | "manual"];
+	layer: <num>;
+	time: <time>; 
+	style: <style>;
+	@: {... dialog lines ...};
+	
+	To have a subtitle displayed the minimal set of fields required are:
+	- time.start
+	- time.stop 
+	- @
+	
+	About dialog lines
+	------------------
+	
+	All white-space will be compressed into one space character.
+	
+	Special characters can be enforced:
+	- new-line: \n
+	- non-breaking space: \h
+	
+	Empty space will be completly removed at these places:
+	- before and after outermost text block brackets: 
+		@ { --> here <-- Hello World! --> here <-- }
+	- between the inner closing and opening brackets of overrides:
+		[s1] --> here <-- {Hello World!}
+	- around forced new-lines:
+		Hello --> here <-- \n --> here <-- World!
+		
+	When neighboring spaces have different styles, the style of the trailing space is used:
+		#u {font.underline: "true"};
+		#s {font.strikethrough: "true"};
+		[u] { Hello } 
+		[s] { World! } 
+		=> 
+		Hello_World!-
+		______-------
+
+	These special characters have to be escaped with a backslash: 
+		{ } [ ] \
+	
+	Style overrides
+	---------------
+
+	[refs] {... more dialog text ...}
+	
+	"name" as in type#name ... is unimportant and ignored.
+	
+	If there is a text block right after an override, the new style will be used only inside 
+	that block.
+	
+	Style and time offsets are restored at the end of text blocks.
+	
+	Examples:
+
+	[s1] {s1 applies only to this block.}
+
+	[s2] s2 applies till the end of the container block.
+	
+	[s1 s2 {color: red;} s3] {Multiple styles are valid just like in a normal ref list}
+
+	{
+		Nesting text blocks. Some may have overrides, others may not.
+
+		[s1]
+		{
+			Every text block will be trimmed and white-space compressed, 
+			so we can use the space freely to make it a bit more readable.
+
+			{Yet another nested block}
+		}
+	}
+	
+	Animation:
+	
+	The override is style + animation mixed.
+	
+	[{time.stop: +00:00:01.000; transition: "linear"; font.size: 20;}]
+	{
+		Here the size of the font gradually becomes 20 after one second.
+	}
+	
+	[{font.color: white;}]
+	{
+		[{time.stop: +00:00:10.000; font.color: black;}]
+		{Text turns from white to black in the first 10 seconds}
+
+		[a1]
+		{This one does whatever a1 tells to do}
+	}
+	
+	[{time.start: "start"; time.stop: "stop"; font.size: 20}]
+	{
+		This is a way to refer to the subtitle's own start and stop values.
+		
+		You can predefine it for easier use:
+		
+		#a1 {time.start: "start"; time.stop: "stop";};
+		[a1 {font.size: 20}] {...}
+	}
+	
+	Karaoke effect using animation:
+
+	[{background.size: 0;}]
+	{
+		[{time.start: 0s; time.stop: +1s; background.size: 10;}]
+		{Ka}
+		[{time.start: 1s; time.stop: +1s; background.size: 10;}]
+		{ra}
+		[{time.start: 2s; time.stop: +1s; background.size: 10;}]
+		{o}
+		[{time.start: 3s; time.stop: +1s; background.size: 10;}]
+		{ke!}
+	}
+
+	To simplify the above, we can predefine the common parameters:
+
+	#s1 {background.size: 0;};
+	#a1 {time.start: +0s; time.stop: +1s; background.size: 10;};
+
+	[s1]
+	{
+		[a1]{Ka}[a1]{ra}[a1]{o}[a1 {font.color: red;}]{ke!}
+
+		// That last piece has even turned into a red in the end :)
+
+		// And to make sure s1 has not been red already, we could derive it 
+		// from "whitefont" (defined somewhere else as: #whitefont {font.color: white;};)
+
+		[a1]{Ka}[a1]{ra}[a1]{o}[a1 whitefont {font.color: red;};}]{ke!} 
+	}
+	
+	When specifying multiple style references watch out for the start/stop values
+	of time. Since they override eachother you should only set them once.
+
+	#a2 {font.color.a: 0;};
+	#a3 {placement.angle.x: 360;};
+
+	[s1] [a1 a2 a3]{Ka}[a1 a2 a3]{ra}[a1 a2 a3]{o}[a1 a2 a3]{ke!}
+
+	If you want to be even lazier:
+
+	#a4: a1 a2 a3;
+
+	[s1] [a4]{Ka}[a4]{ra}[a4]{o}[a4]{ke!}	
+	
+	To avoid the times overriding eachother use either nested text blocks ...
+	
+	#a2 {time.start: +0.5s; time.stop: +1s; font.color.a: 0;};
+	#a3 {time.start: +1s; time.stop: +1s; placement.angle.x: 360;};
+	
+	[s1] [a1]{[a2]{[a3]Ka}}[a1]{[a2]{[a3]ra}}[a1]{[a2]{[a3]o}}[a1]{[a2]{[a3]ke}}
+	
+	... or a list of references were each has a different time.id ...
+
+	#a1 {time {id: 1; start: +0s; stop: +1s;}; background.size: 10;};
+	#a2 {time {id: 2; start: +0.5s; stop: +1s;}; font.color.a: 0;};
+	#a3 {time {id: 3; start: +1s; stop: +1s;}; placement.angle.x: 360;};
+	
+	[s1] [a1,a2,a3]{Ka}[a1,a2,a3]{ra}[a1,a2,a3]{o}[a1,a2,a3]{ke!}
+	
+	... or just let it auto-number the ids, each timer id becomes the actual position 
+	in the comma separated list (a1 id => 1, a2 id => 2, a3 id => 3).
+
+	#a1 {time {start: +0s; stop: +1s;}; background.size: 10;};
+	#a2 {time {start: +0.5s; stop: +1s;}; font.color.a: 0;};
+	#a3 {time {start: +1s; stop: +1s;}; placement.angle.x: 360;};
+	
+	[s1] [a1,a2,a3]{Ka}[a1,a2,a3]{ra}[a1,a2,a3]{o}[a1,a2,a3]{ke!}
+
+	It is also possible to leave some of the ref list slots empty.
+
+	[s1] [a1,a2,a3]{Ka}[,a2,a3]{ra}[a1,,a3]{o}[a1,a2,]{ke!}
+	
+	Text includes
+	-------------
+
+	Text blocks can also be defined outside subtitles and included later. 
+	
+	#hw { @ {[{font.italic: "true"}]Hello World!}; };
+	subtitle { @ {[hw]}; };
+	
+	When mixing style overrides and text includes, the new style applies to the text too.
+	
+	#hw { @ {Hello World!}; };
+	subtitle { @ {[hw {font.italic: "true"}]}; };
+	
+	Multiple levels of recursion:
+	
+	#dblspace { @ {[{font.scale.cx: 2;}]             }; }; // note: there is still only one space character here because of white space compression
+	#hw { @ {Hello[dblspace]World!}; };
+	subtitle { @ {[hw]}; };
+};
+
+Defaults and predefined names
+-----------------------------
+
+These must be predefined by the application and always assumed to be there for ssf version 1.
+
+color#white {a: 255; r: 255; g: 255; b: 255;};
+color#black {a: 255; r: 0; g: 0; b: 0;};
+color#gray {a: 255; r: 128; g: 128; b: 128;}; 
+color#red {a: 255; r: 255; g: 0; b: 0;};
+color#green {a: 255; r: 0; g: 255; b: 0;};
+color#blue {a: 255; r: 0; g: 0; b: 255;};
+color#cyan {a: 255; r: 0; g: 255; b: 255;};
+color#yellow {a: 255; r: 255; g: 255; b: 0;};
+color#magenta {a: 255; r: 255; g: 0; b: 255;};
+
+align#topleft {v: "top"; h: "left";};
+align#topcenter {v: "top"; h: "center";};
+align#topright {v: "top"; h: "right";};
+align#middleleft {v: "middle"; h: "left";};
+align#middlecenter {v: "middle"; h: "center";};
+align#middleright {v: "middle"; h: "right";};
+align#bottomleft {v: "bottom"; h: "left";};
+align#bottomcenter {v: "bottom"; h: "center";};
+align#bottomright {v: "bottom"; h: "right";};
+
+time#time {scale: 1;};
+time#startstop {start: "start"; stop: "stop";};
+
+#b {font.weight: "bold"};
+#i {font.italic: "true"};
+#u {font.underline: "true"};
+#s {font.strikethrough: "true"};
+
+#nobr {linebreak: "none"};
+
+subtitle#subtitle
+{
+	frame
+	{
+		reference: "video"; 
+		resolution: {cx: 640; cy: 480;};
+	};
+	
+	direction
+	{
+		primary: "right";
+		secondary: "down";
+	};
+
+	wrap: "normal";
+	
+	layer: 0;
+	
+	style
+	{
+		linebreak: "word";
+	
+		placement
+		{
+			clip: "none";
+			margin: {t: 0; r: 0; b: 0; l: 0;};
+			align: bottomcenter;
+			pos: "auto";
+			offset: {x: 0; y: 0;};
+			angle: {x: 0; y: 0; z: 0;};
+		};
+		
+		font
+		{
+			face: "Arial"; 
+			size: 20; 
+			weight: "bold"; 
+			color: white; 
+			underline: "false"; 
+			strikethrough: "false"; 
+			italic: "false"; 
+			spacing: 0; 
+			scale: {cx: 1; cy: 1;};
+			kerning: "true";
+		};
+
+		background
+		{
+			color: black; 
+			size: 2;
+			type: "outline";
+		};
+
+		shadow
+		{
+			color: black {a: 128;}; 
+			depth: 2;
+			angle: -45;
+			blur: 0;
+		};
+		
+		fill
+		{
+			color: yellow;
+			width: 0;
+		};
+	};
+};
+
+Streaming
+---------
+
+Correct packetization is important when a subtitle file has to be embeded into a media 
+file. Putting everything into the header would be trivial, but sending the whole track 
+data ahead cannot be called streaming really, and it also makes editing impossible 
+unless the application learns how to parse and resave our format. 
+
+The recommended way of segmenting ssf into media samples:
+- Search top level definitions which do not satisfy the requirements of a displayable 
+subtitle (one of start/stop/@ is absent) and save them into the file header of the media 
+file as the initializer data for playback.
+- Multiplex the rest of the top level definitions as media samples, their timestamps 
+shall be used for interleaving with other streams.
+
+Example:
+
+#mystyle {font.face: "Times New Roman";};
+subtitle#s1 {time.start: 2s;};
+subtitle#s2 : s1 {style: mystyle; time.stop: +1s; @ {2s -> 3s};};
+subtitle#s3 {style: mystyle; time.start: 5s; @ {5s -> 7s};};
+subtitle#s4 : s3 {time.stop: +2s;};
+
+File header:
+
+#mystyle {font.face: "Times New Roman";};
+subtitle#s1 {time.start: 2s;};
+subtitle#s3 {style: mystyle; time.start: 5s; @ {5s -> 7s};};
+
+Media samples:
+
+subtitle#s2 : s1 {style: mystyle; time.stop: +1s; @ {2s -> 3s};};
+subtitle#s4 : s3 {time.stop: +2s;};
diff --git a/vsfilter/libssf/libssf.vcproj b/vsfilter/libssf/libssf.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..c4b150e58215f32e24592276fb2e780f120e186f
--- /dev/null
+++ b/vsfilter/libssf/libssf.vcproj
@@ -0,0 +1,331 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="libssf"
+	ProjectGUID="{971851AB-A612-4F2F-866F-F0FA152EFFB2}"
+	RootNamespace="libssf"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<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=".\Arabic.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Array.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Exception.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\File.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\FontWrapper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Glyph.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\GlyphPath.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Node.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\NodeFactory.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Rasterizer.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Renderer.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Split.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\Stream.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\StringMap.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Subtitle.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SubtitleFile.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\Arabic.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Array.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Exception.h"
+				>
+			</File>
+			<File
+				RelativePath=".\File.h"
+				>
+			</File>
+			<File
+				RelativePath=".\FontWrapper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Glyph.h"
+				>
+			</File>
+			<File
+				RelativePath=".\GlyphPath.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Node.h"
+				>
+			</File>
+			<File
+				RelativePath=".\NodeFactory.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Rasterizer.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Renderer.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Split.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Stream.h"
+				>
+			</File>
+			<File
+				RelativePath=".\StringMap.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Subtitle.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SubtitleFile.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>
+		<File
+			RelativePath=".\demo\demo.ssa"
+			>
+		</File>
+		<File
+			RelativePath=".\demo\demo.ssf"
+			>
+		</File>
+		<File
+			RelativePath=".\docs\ssf-specs.txt"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vsfilter/libssf/stdafx.cpp b/vsfilter/libssf/stdafx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b29179f6b5d4e28d7945d7698e68940418d73241
--- /dev/null
+++ b/vsfilter/libssf/stdafx.cpp
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// libssf.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/vsfilter/libssf/stdafx.h b/vsfilter/libssf/stdafx.h
new file mode 100644
index 0000000000000000000000000000000000000000..96183bb0fb0848aa224774dceb15646fc4c1c2eb
--- /dev/null
+++ b/vsfilter/libssf/stdafx.h
@@ -0,0 +1,68 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER				// Allow use of features specific to Windows XP or later.
+#define WINVER 0x0501		// Change this to the appropriate value to target other versions of Windows.
+#endif
+
+#ifndef _WIN32_WINNT		// Allow use of features specific to Windows XP or later.                   
+#define _WIN32_WINNT 0x0501	// Change this to the appropriate value to target other versions of Windows.
+#endif						
+
+#ifndef _WIN32_WINDOWS		// Allow use of features specific to Windows 98 or later.
+#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+#ifndef _WIN32_IE			// Allow use of features specific to IE 6.0 or later.
+#define _WIN32_IE 0x0600	// Change this to the appropriate value to target other versions of IE.
+#endif
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC core and standard components
+#include <atlcoll.h>         // MFC core and standard components
+
+// TODO: reference additional headers your program requires here
+
+#include <streams.h>
+#include "../dsutil/DSUtil.h"
+#include "../dsutil/vd.h"
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
diff --git a/vsfilter/clipbug-patch-take3.patch b/vsfilter/patchfiles/clipbug-patch-take3.patch
similarity index 100%
rename from vsfilter/clipbug-patch-take3.patch
rename to vsfilter/patchfiles/clipbug-patch-take3.patch
diff --git a/vsfilter/patchfiles/csriapi.cpp b/vsfilter/patchfiles/csriapi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c6b72298107747f097896e878b00e0e1c67e00b7
--- /dev/null
+++ b/vsfilter/patchfiles/csriapi.cpp
@@ -0,0 +1,221 @@
+/* 
+ *	Copyright (C) 2007 Niels Martin Hansen
+ *	http://aegisub.net/
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <afxdlgs.h>
+#include <atlpath.h>
+#include "resource.h"
+#include "..\..\..\subtitles\VobSubFile.h"
+#include "..\..\..\subtitles\RTS.h"
+#include "..\..\..\subtitles\SSF.h"
+#include "..\..\..\SubPic\MemSubPic.h"
+
+// Be sure to have <csri/csri.h> in include path
+#define CSRIAPI extern "C" __declspec(dllexport)
+#define CSRI_OWN_HANDLES
+typedef const char *csri_rend;
+extern "C" struct csri_vsfilter_inst {
+	CRenderedTextSubtitle *rts;
+	CCritSec *cs;
+	CSize script_res;
+	CSize screen_res;
+	CRect video_rect;
+	enum csri_pixfmt pixfmt;
+	size_t readorder;
+};
+typedef struct csri_vsfilter_inst csri_inst;
+#include <csri/csri.h>
+static csri_rend csri_vsfilter = "vsfilter";
+
+
+CSRIAPI csri_inst *csri_open_file(csri_rend *renderer, const char *filename, struct csri_openflag *flags)
+{
+	int namesize;
+	wchar_t *namebuf;
+
+	namesize = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
+	if (!namesize)
+		return 0;
+	namesize++;
+	namebuf = new wchar_t[namesize];
+	MultiByteToWideChar(CP_UTF8, 0, filename, -1, namebuf, namesize);
+
+	csri_inst *inst = new csri_inst();
+	inst->cs = new CCritSec();
+	inst->rts = new CRenderedTextSubtitle(inst->cs);
+	if (inst->rts->Open(CString(namebuf), DEFAULT_CHARSET)) {
+		delete[] namebuf;
+		inst->readorder = 0;
+		return inst;
+	} else {
+		delete[] namebuf;
+		delete inst->rts;
+		delete inst->cs;
+		delete inst;
+		return 0;
+	}
+}
+
+
+CSRIAPI csri_inst *csri_open_mem(csri_rend *renderer, const void *data, size_t length, struct csri_openflag *flags)
+{
+	// This is actually less effecient than opening a file, since this first writes the memory data to a temp file,
+	// then opens that file and parses from that.
+	csri_inst *inst = new csri_inst();
+	inst->cs = new CCritSec();
+	inst->rts = new CRenderedTextSubtitle(inst->cs);
+	if (inst->rts->Open((BYTE*)data, (int)length, DEFAULT_CHARSET, _T("CSRI memory subtitles"))) {
+		inst->readorder = 0;
+		return inst;
+	} else {
+		delete inst->rts;
+		delete inst->cs;
+		delete inst;
+		return 0;
+	}
+}
+
+
+CSRIAPI void csri_close(csri_inst *inst)
+{
+	if (!inst) return;
+
+	delete inst->rts;
+	delete inst->cs;
+	delete inst;
+}
+
+
+CSRIAPI int csri_request_fmt(csri_inst *inst, const struct csri_fmt *fmt)
+{
+	if (!inst) return -1;
+
+	if (!fmt->width || !fmt->height)
+		return -1;
+
+	// Check if pixel format is supported
+	switch (fmt->pixfmt) {
+		case CSRI_F_BGR_:
+		case CSRI_F_BGR:
+		case CSRI_F_YUY2:
+		case CSRI_F_YV12:
+			inst->pixfmt = fmt->pixfmt;
+			break;
+
+		default:
+			return -1;
+	}
+	inst->screen_res = CSize(fmt->width, fmt->height);
+	inst->video_rect = CRect(0, 0, fmt->width, fmt->height);
+	return 0;
+}
+
+
+CSRIAPI void csri_render(csri_inst *inst, struct csri_frame *frame, double time)
+{
+	const double arbitrary_framerate = 25.0;
+	SubPicDesc spd;
+	spd.w = inst->screen_res.cx;
+	spd.h = inst->screen_res.cy;
+	switch (inst->pixfmt) {
+		case CSRI_F_BGR_:
+			spd.type = MSP_RGB32;
+			spd.bpp = 32;
+			spd.bits = frame->planes[0];
+			spd.pitch = frame->strides[0];
+			break;
+
+		case CSRI_F_BGR:
+			spd.type = MSP_RGB24;
+			spd.bpp = 24;
+			spd.bits = frame->planes[0];
+			spd.pitch = frame->strides[0];
+			break;
+
+		case CSRI_F_YUY2:
+			spd.type = MSP_YUY2;
+			spd.bpp = 16;
+			spd.bits = frame->planes[0];
+			spd.pitch = frame->strides[0];
+			break;
+
+		case CSRI_F_YV12:
+			spd.type = MSP_YV12;
+			spd.bpp = 12;
+			spd.bits = frame->planes[0];
+			spd.bitsU = frame->planes[1];
+			spd.bitsV = frame->planes[2];
+			spd.pitch = frame->strides[0];
+			spd.pitchUV = frame->strides[1];
+			break;
+
+		default:
+			// eh?
+			return;
+	}
+	spd.vidrect = inst->video_rect;
+
+	inst->rts->Render(spd, (REFERENCE_TIME)(time*10000000), arbitrary_framerate, inst->video_rect);
+}
+
+
+// No extensions supported
+CSRIAPI void *csri_query_ext(csri_rend *rend, csri_ext_id extname)
+{
+	return 0;
+}
+
+// Get info for renderer
+static struct csri_info csri_vsfilter_info = {
+	"vsfilter_textsub", // name
+	"2.38-0611-3", // version (assumed version number, svn revision, patchlevel)
+	// 2.38-0611 is base svn 611
+	// 2.38-0611-1 is with clipfix and fax/fay patch
+	// 2.38-0611-2 adds CSRI
+	// 2.38-0611-3 fixes a bug in CSRI and adds fontcrash-fix and float-pos
+	"VSFilter/TextSub (SVN 611 + patches for clipfix, fax/fay, fontcrash-fix, float-pos and CSRI)", // longname
+	"Gabest", // author
+	"Copyright (c) 2004-2007 by Gabest and others" // copyright
+};
+CSRIAPI struct csri_info *csri_renderer_info(csri_rend *rend)
+{
+	return &csri_vsfilter_info;
+}
+// Only one supported, obviously
+CSRIAPI csri_rend *csri_renderer_byname(const char *name, const char *specific)
+{
+	if (strcmp(name, csri_vsfilter_info.name))
+		return 0;
+	if (specific && strcmp(specific, csri_vsfilter_info.specific))
+		return 0;
+	return &csri_vsfilter;
+}
+// Still just one
+CSRIAPI csri_rend *csri_renderer_default()
+{
+	return &csri_vsfilter;
+}
+// And no further
+CSRIAPI csri_rend *csri_renderer_next(csri_rend *prev)
+{
+	return 0;
+}
+
diff --git a/vsfilter/fax_fay-SVN611.patch b/vsfilter/patchfiles/fax_fay-SVN611.patch
similarity index 100%
rename from vsfilter/fax_fay-SVN611.patch
rename to vsfilter/patchfiles/fax_fay-SVN611.patch
diff --git a/vsfilter/floatpos.patch b/vsfilter/patchfiles/floatpos.patch
similarity index 100%
rename from vsfilter/floatpos.patch
rename to vsfilter/patchfiles/floatpos.patch
diff --git a/vsfilter/fontcrash.patch b/vsfilter/patchfiles/fontcrash.patch
similarity index 100%
rename from vsfilter/fontcrash.patch
rename to vsfilter/patchfiles/fontcrash.patch
diff --git a/vsfilter/readme.txt b/vsfilter/patchfiles/readme.txt
similarity index 86%
rename from vsfilter/readme.txt
rename to vsfilter/patchfiles/readme.txt
index 6c0320aad6890c82a329a12a2b33ae414c92043c..b44221d400791b5d09d79f5cdae241421bffdfe4 100644
--- a/vsfilter/readme.txt
+++ b/vsfilter/patchfiles/readme.txt
@@ -14,3 +14,6 @@ Patch applied in guliverkli/src/filters/transform/vsfilter/:
 
 File added to guliverkli/src/filters/transform/vsfilter/:
   csriapi.cpp  [also add to project file]
+
+The copy of VSFilter in the directory above this already has the patches
+applied.
diff --git a/vsfilter/plugins.cpp b/vsfilter/plugins.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90ed7b195e4ca8eb1895a0ceb14932794f9cfe00
--- /dev/null
+++ b/vsfilter/plugins.cpp
@@ -0,0 +1,732 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <afxdlgs.h>
+#include <atlpath.h>
+#include "resource.h"
+#include "subtitles/VobSubFile.h"
+#include "subtitles/RTS.h"
+#include "subtitles/SSF.h"
+#include "SubPic/MemSubPic.h"
+
+//
+// Generic interface
+//
+
+namespace Plugin
+{
+
+class CFilter : public CAMThread, public CCritSec
+{
+private:
+	CString m_fn;
+
+protected:
+	float m_fps;
+	CCritSec m_csSubLock;
+	CComPtr<ISubPicQueue> m_pSubPicQueue;
+	CComPtr<ISubPicProvider> m_pSubPicProvider;
+	DWORD_PTR m_SubPicProviderId;
+
+public:
+	CFilter() : m_fps(-1), m_SubPicProviderId(0) {CAMThread::Create();}
+	virtual ~CFilter() {CAMThread::CallWorker(0);}
+
+	CString GetFileName() {CAutoLock cAutoLock(this); return m_fn;}
+	void SetFileName(CString fn) {CAutoLock cAutoLock(this); m_fn = fn;}
+
+	bool Render(SubPicDesc& dst, REFERENCE_TIME rt, float fps)
+	{
+		if(!m_pSubPicProvider)
+			return(false);
+
+		CSize size(dst.w, dst.h);
+
+		if(!m_pSubPicQueue)
+		{
+			CComPtr<ISubPicAllocator> pAllocator = new CMemSubPicAllocator(dst.type, size);
+
+			HRESULT hr;
+			if(!(m_pSubPicQueue = new CSubPicQueueNoThread(pAllocator, &hr)) || FAILED(hr))
+			{
+				m_pSubPicQueue = NULL;
+				return(false);
+			}
+		}
+
+		if(m_SubPicProviderId != (DWORD_PTR)(ISubPicProvider*)m_pSubPicProvider)
+		{
+			m_pSubPicQueue->SetSubPicProvider(m_pSubPicProvider);
+			m_SubPicProviderId = (DWORD_PTR)(ISubPicProvider*)m_pSubPicProvider;
+		}
+
+		CComPtr<ISubPic> pSubPic;
+		if(!m_pSubPicQueue->LookupSubPic(rt, &pSubPic))
+			return(false);
+
+		CRect r;
+		pSubPic->GetDirtyRect(r);
+
+		if(dst.type == MSP_RGB32 || dst.type == MSP_RGB24 || dst.type == MSP_RGB16 || dst.type == MSP_RGB15)
+			dst.h = -dst.h;
+
+		pSubPic->AlphaBlt(r, r, &dst);
+
+		return(true);
+	}
+
+	DWORD ThreadProc()
+	{
+		SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST);
+
+		CAtlArray<HANDLE> handles;
+		handles.Add(GetRequestHandle());
+
+		CString fn = GetFileName();
+		CFileStatus fs;
+		fs.m_mtime = 0;
+		CFileGetStatus(fn, fs);
+
+		while(1)
+		{
+			DWORD i = WaitForMultipleObjects(handles.GetCount(), handles.GetData(), FALSE, 1000);
+
+			if(WAIT_OBJECT_0 == i)
+			{
+				Reply(S_OK);
+				break;
+			}
+			else if(WAIT_OBJECT_0 + 1 >= i && i <= WAIT_OBJECT_0 + handles.GetCount())
+			{
+				if(FindNextChangeNotification(handles[i - WAIT_OBJECT_0]))
+				{
+					CFileStatus fs2;
+					fs2.m_mtime = 0;
+					CFileGetStatus(fn, fs2);
+
+					if(fs.m_mtime < fs2.m_mtime)
+					{
+						fs.m_mtime = fs2.m_mtime;
+
+						if(CComQIPtr<ISubStream> pSubStream = m_pSubPicProvider)
+						{
+							CAutoLock cAutoLock(&m_csSubLock);
+							pSubStream->Reload();
+						}
+					}
+				}
+			}
+			else if(WAIT_TIMEOUT == i)
+			{
+				CString fn2 = GetFileName();
+
+				if(fn != fn2)
+				{
+					CPath p(fn2);
+					p.RemoveFileSpec();
+					HANDLE h = FindFirstChangeNotification(p, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE); 
+					if(h != INVALID_HANDLE_VALUE)
+					{
+						fn = fn2;
+						handles.SetCount(1);
+						handles.Add(h);
+					}
+				}
+			}
+			else // if(WAIT_ABANDONED_0 == i || WAIT_FAILED == i)
+			{
+				break;
+			}
+		}
+
+		m_hThread = 0;
+
+		for(int i = 1; i < handles.GetCount(); i++)
+			FindCloseChangeNotification(handles[i]);
+
+		return 0;
+	}
+};
+
+class CVobSubFilter : virtual public CFilter
+{
+public:
+	CVobSubFilter(CString fn = _T(""))
+	{
+		if(!fn.IsEmpty()) Open(fn);
+	}
+
+	bool Open(CString fn)
+	{
+		SetFileName(_T(""));
+		m_pSubPicProvider = NULL;
+
+		if(CVobSubFile* vsf = new CVobSubFile(&m_csSubLock))
+		{
+			m_pSubPicProvider = (ISubPicProvider*)vsf;
+			if(vsf->Open(CString(fn))) SetFileName(fn);
+			else m_pSubPicProvider = NULL;
+		}
+
+		return !!m_pSubPicProvider;
+	}
+};
+
+class CTextSubFilter : virtual public CFilter
+{
+	int m_CharSet;
+
+public:
+	CTextSubFilter(CString fn = _T(""), int CharSet = DEFAULT_CHARSET, float fps = -1)
+		: m_CharSet(CharSet)
+	{
+		m_fps = fps;
+		if(!fn.IsEmpty()) Open(fn, CharSet);
+	}
+
+	int GetCharSet() {return(m_CharSet);}
+
+	bool Open(CString fn, int CharSet = DEFAULT_CHARSET)
+	{
+		SetFileName(_T(""));
+		m_pSubPicProvider = NULL;
+
+		if(!m_pSubPicProvider)
+		{
+			if(ssf::CRenderer* ssf = new ssf::CRenderer(&m_csSubLock))
+			{
+				m_pSubPicProvider = (ISubPicProvider*)ssf;
+				if(ssf->Open(CString(fn))) SetFileName(fn);
+				else m_pSubPicProvider = NULL;
+			}
+		}
+
+		if(!m_pSubPicProvider)
+		{
+			if(CRenderedTextSubtitle* rts = new CRenderedTextSubtitle(&m_csSubLock))
+			{
+				m_pSubPicProvider = (ISubPicProvider*)rts;
+				if(rts->Open(CString(fn), CharSet)) SetFileName(fn);
+				else m_pSubPicProvider = NULL;
+			}
+		}
+
+		return !!m_pSubPicProvider;
+	}
+};
+
+//
+// VirtualDub interface
+//
+
+namespace VirtualDub
+{
+	#include "include/VirtualDub/VirtualDub.h"
+
+	class CVirtualDubFilter : virtual public CFilter
+	{
+	public:
+		CVirtualDubFilter() {}
+		virtual ~CVirtualDubFilter() {}
+
+		virtual int RunProc(const FilterActivation* fa, const FilterFunctions* ff)
+		{
+			SubPicDesc dst;
+			dst.type = MSP_RGB32;
+			dst.w = fa->src.w;
+			dst.h = fa->src.h;
+			dst.bpp = 32;
+			dst.pitch = fa->src.pitch;
+			dst.bits = (LPVOID)fa->src.data;
+
+			Render(dst, 10000i64*fa->pfsi->lSourceFrameMS, (float)1000 / fa->pfsi->lMicrosecsPerFrame);
+
+			return 0;
+		}
+
+		virtual long ParamProc(FilterActivation* fa, const FilterFunctions* ff)
+		{
+			fa->dst.offset	= fa->src.offset;
+			fa->dst.modulo	= fa->src.modulo;
+			fa->dst.pitch	= fa->src.pitch;
+
+			return 0;
+		}
+
+		virtual int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd) = 0;
+		virtual void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str) = 0;
+		virtual bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen) = 0;
+	};
+
+	class CVobSubVirtualDubFilter : public CVobSubFilter, public CVirtualDubFilter
+	{
+	public:
+		CVobSubVirtualDubFilter(CString fn = _T("")) 
+			: CVobSubFilter(fn) {}
+
+		int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd)
+		{
+			AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+			CFileDialog fd(TRUE, NULL, GetFileName(), OFN_EXPLORER|OFN_ENABLESIZING|OFN_HIDEREADONLY, 
+				_T("VobSub files (*.idx;*.sub)|*.idx;*.sub||"), CWnd::FromHandle(hwnd), 0);
+
+			if(fd.DoModal() != IDOK) return 1;
+
+			return Open(fd.GetPathName()) ? 0 : 1;
+		}
+
+		void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str)
+		{
+			sprintf(str, " (%s)", !GetFileName().IsEmpty() ? CStringA(GetFileName()) : " (empty)");
+		}
+
+		bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen)
+		{
+			CStringA fn(GetFileName());
+			fn.Replace("\\", "\\\\");
+			_snprintf(buf, buflen, "Config(\"%s\")", fn);
+			return(true);
+		}
+	};
+
+	class CTextSubVirtualDubFilter : public CTextSubFilter, public CVirtualDubFilter
+	{
+	public:
+		CTextSubVirtualDubFilter(CString fn = _T(""), int CharSet = DEFAULT_CHARSET) 
+			: CTextSubFilter(fn, CharSet) {}
+
+		int ConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd)
+		{
+			AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+			const TCHAR formats[] = _T("TextSub files (*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt)|*.sub;*.srt;*.smi;*.ssa;*.ass;*.xss;*.psb;*.txt||");
+			CFileDialog fd(TRUE, NULL, GetFileName(), OFN_EXPLORER|OFN_ENABLESIZING|OFN_HIDEREADONLY|OFN_ENABLETEMPLATE|OFN_ENABLEHOOK, 
+				formats, CWnd::FromHandle(hwnd), OPENFILENAME_SIZE_VERSION_400 /*0*/);
+
+			UINT CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam);
+
+			fd.m_pOFN->hInstance = AfxGetResourceHandle();
+			fd.m_pOFN->lpTemplateName = MAKEINTRESOURCE(IDD_TEXTSUBOPENTEMPLATE);
+			fd.m_pOFN->lpfnHook = OpenHookProc;
+			fd.m_pOFN->lCustData = (LPARAM)DEFAULT_CHARSET;
+
+			if(fd.DoModal() != IDOK) return 1;
+
+			return Open(fd.GetPathName(), fd.m_pOFN->lCustData) ? 0 : 1;
+		}
+
+		void StringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str)
+		{
+			if(!GetFileName().IsEmpty()) sprintf(str, " (%s, %d)", CStringA(GetFileName()), GetCharSet());
+			else sprintf(str, " (empty)");
+		}
+
+		bool FssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen)
+		{
+			CStringA fn(GetFileName());
+			fn.Replace("\\", "\\\\");
+			_snprintf(buf, buflen, "Config(\"%s\", %d)", fn, GetCharSet());
+			return(true);
+		}
+	};
+
+	int vobsubInitProc(FilterActivation* fa, const FilterFunctions* ff)
+	{
+		return !(*(CVirtualDubFilter**)fa->filter_data = new CVobSubVirtualDubFilter());
+	}
+
+	int textsubInitProc(FilterActivation* fa, const FilterFunctions* ff)
+	{
+		return !(*(CVirtualDubFilter**)fa->filter_data = new CTextSubVirtualDubFilter());
+	}
+
+	void baseDeinitProc(FilterActivation* fa, const FilterFunctions* ff)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		if(f) delete f, f = NULL;
+	}
+
+	int baseRunProc(const FilterActivation* fa, const FilterFunctions* ff)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		return f ? f->RunProc(fa, ff) : 1;
+	}
+
+	long baseParamProc(FilterActivation* fa, const FilterFunctions* ff)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		return f ? f->ParamProc(fa, ff) : 1;
+	}
+
+	int baseConfigProc(FilterActivation* fa, const FilterFunctions* ff, HWND hwnd)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		return f ? f->ConfigProc(fa, ff, hwnd) : 1;
+	}
+
+	void baseStringProc(const FilterActivation* fa, const FilterFunctions* ff, char* str)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		if(f) f->StringProc(fa, ff, str);
+	}
+
+	bool baseFssProc(FilterActivation* fa, const FilterFunctions* ff, char* buf, int buflen)
+	{
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		return f ? f->FssProc(fa, ff, buf, buflen) : false;
+	}
+
+	void vobsubScriptConfig(IScriptInterpreter* isi, void* lpVoid, CScriptValue* argv, int argc)
+	{
+		FilterActivation* fa = (FilterActivation*)lpVoid;
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		if(f) delete f;
+		f = new CVobSubVirtualDubFilter(CString(*argv[0].asString()));
+		*(CVirtualDubFilter**)fa->filter_data = f;
+	}
+
+	void textsubScriptConfig(IScriptInterpreter* isi, void* lpVoid, CScriptValue* argv, int argc)
+	{
+		FilterActivation* fa = (FilterActivation*)lpVoid;
+		CVirtualDubFilter* f = *(CVirtualDubFilter**)fa->filter_data;
+		if(f) delete f;
+		f = new CTextSubVirtualDubFilter(CString(*argv[0].asString()), argv[1].asInt());
+		*(CVirtualDubFilter**)fa->filter_data = f;
+	}
+
+	ScriptFunctionDef vobsub_func_defs[]={
+		{ (ScriptFunctionPtr)vobsubScriptConfig, "Config", "0s" },
+		{ NULL },
+	};
+
+	CScriptObject vobsub_obj={
+		NULL, vobsub_func_defs
+	};
+
+	struct FilterDefinition filterDef_vobsub = 
+	{
+		NULL, NULL, NULL,       // next, prev, module
+		"VobSub",				// name
+		"Adds subtitles from a vob sequence.", // desc
+		"Gabest",               // maker
+		NULL,                   // private_data
+		sizeof(CVirtualDubFilter**), // inst_data_size
+		vobsubInitProc,         // initProc
+		baseDeinitProc,			// deinitProc
+		baseRunProc,			// runProc
+		baseParamProc,			// paramProc
+		baseConfigProc,			// configProc
+		baseStringProc,			// stringProc
+		NULL,					// startProc
+		NULL,					// endProc
+		&vobsub_obj,			// script_obj
+		baseFssProc,			// fssProc
+	};
+
+	ScriptFunctionDef textsub_func_defs[]={
+		{ (ScriptFunctionPtr)textsubScriptConfig, "Config", "0si" },
+		{ NULL },
+	};
+
+	CScriptObject textsub_obj={
+		NULL, textsub_func_defs
+	};
+
+	struct FilterDefinition filterDef_textsub = 
+	{
+		NULL, NULL, NULL,       // next, prev, module
+		"TextSub",				// name
+		"Adds subtitles from srt, sub, psb, smi, ssa, ass file formats.", // desc
+		"Gabest",               // maker
+		NULL,                   // private_data
+		sizeof(CVirtualDubFilter**), // inst_data_size
+		textsubInitProc,        // initProc
+		baseDeinitProc,			// deinitProc
+		baseRunProc,			// runProc
+		baseParamProc,			// paramProc
+		baseConfigProc,			// configProc
+		baseStringProc,			// stringProc
+		NULL,					// startProc
+		NULL,					// endProc
+		&textsub_obj,			// script_obj
+		baseFssProc,			// fssProc
+	};
+
+	static FilterDefinition* fd_vobsub;
+	static FilterDefinition* fd_textsub;
+
+	extern "C" __declspec(dllexport) int __cdecl VirtualdubFilterModuleInit2(FilterModule *fm, const FilterFunctions *ff, int& vdfd_ver, int& vdfd_compat)
+	{
+		if(!(fd_vobsub = ff->addFilter(fm, &filterDef_vobsub, sizeof(FilterDefinition)))
+		|| !(fd_textsub = ff->addFilter(fm, &filterDef_textsub, sizeof(FilterDefinition))))
+			return 1;
+
+		vdfd_ver = VIRTUALDUB_FILTERDEF_VERSION;
+		vdfd_compat = VIRTUALDUB_FILTERDEF_COMPATIBLE;
+
+		return 0;
+	}
+
+	extern "C" __declspec(dllexport) void __cdecl VirtualdubFilterModuleDeinit(FilterModule *fm, const FilterFunctions *ff)
+	{
+		ff->removeFilter(fd_textsub);
+		ff->removeFilter(fd_vobsub);
+	}
+}
+
+//
+// Avisynth interface
+//
+
+namespace AviSynth1
+{
+	#include "include/avisynth/avisynth1.h"
+
+	class CAvisynthFilter : public GenericVideoFilter, virtual public CFilter
+	{
+	public:
+		CAvisynthFilter(PClip c, IScriptEnvironment* env) : GenericVideoFilter(c) {}
+
+		PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env)
+		{
+			PVideoFrame frame = child->GetFrame(n, env);
+
+			env->MakeWritable(&frame);
+
+			SubPicDesc dst;
+			dst.w = vi.width;
+			dst.h = vi.height;
+			dst.pitch = frame->GetPitch();
+			dst.bits = (void**)frame->GetWritePtr();
+			dst.bpp = vi.BitsPerPixel();
+			dst.type = 
+				vi.IsRGB32() ? MSP_RGB32 : 
+				vi.IsRGB24() ? MSP_RGB24 : 
+				vi.IsYUY2() ? MSP_YUY2 : 
+				-1;
+
+			float fps = m_fps > 0 ? m_fps : (float)vi.fps_numerator / vi.fps_denominator;
+
+			Render(dst, (REFERENCE_TIME)(10000000i64 * n / fps), fps);
+
+			return(frame);
+		}
+	};
+
+	class CVobSubAvisynthFilter : public CVobSubFilter, public CAvisynthFilter
+	{
+	public:
+		CVobSubAvisynthFilter(PClip c, const char* fn, IScriptEnvironment* env)
+			: CVobSubFilter(CString(fn))
+			, CAvisynthFilter(c, env)
+		{
+			if(!m_pSubPicProvider)
+				env->ThrowError("VobSub: Can't open \"%s\"", fn);
+		}
+	};
+
+	AVSValue __cdecl VobSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CVobSubAvisynthFilter(args[0].AsClip(), args[1].AsString(), env));
+	}
+    
+	class CTextSubAvisynthFilter : public CTextSubFilter, public CAvisynthFilter
+	{
+	public:
+		CTextSubAvisynthFilter(PClip c, IScriptEnvironment* env, const char* fn, int CharSet = DEFAULT_CHARSET, float fps = -1)
+			: CTextSubFilter(CString(fn), CharSet, fps)
+			, CAvisynthFilter(c, env)
+		{
+			if(!m_pSubPicProvider)
+				env->ThrowError("TextSub: Can't open \"%s\"", fn);
+		}
+	};
+
+	AVSValue __cdecl TextSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString()));
+	}
+
+	AVSValue __cdecl TextSubCreateSI(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt()));
+	}
+
+	AVSValue __cdecl TextSubCreateSIF(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt(), args[3].AsFloat()));
+	}
+
+	extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit(IScriptEnvironment* env)
+	{
+		env->AddFunction("VobSub", "cs", VobSubCreateS, 0);
+		env->AddFunction("TextSub", "cs", TextSubCreateS, 0);
+		env->AddFunction("TextSub", "csi", TextSubCreateSI, 0);
+		env->AddFunction("TextSub", "csif", TextSubCreateSIF, 0);
+		return(NULL);
+	}
+}
+
+namespace AviSynth25
+{
+	#include "include/avisynth/avisynth25.h"
+
+	static bool s_fSwapUV = false;
+
+	class CAvisynthFilter : public GenericVideoFilter, virtual public CFilter
+	{
+	public:
+		CAvisynthFilter(PClip c, IScriptEnvironment* env) : GenericVideoFilter(c) {}
+
+		PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env)
+		{
+			PVideoFrame frame = child->GetFrame(n, env);
+
+			env->MakeWritable(&frame);
+
+			SubPicDesc dst;
+			dst.w = vi.width;
+			dst.h = vi.height;
+			dst.pitch = frame->GetPitch();
+			dst.pitchUV = frame->GetPitch(PLANAR_U);
+			dst.bits = (void**)frame->GetWritePtr();
+			dst.bitsU = frame->GetWritePtr(PLANAR_U);
+			dst.bitsV = frame->GetWritePtr(PLANAR_V);
+			dst.bpp = dst.pitch/dst.w*8; //vi.BitsPerPixel();
+			dst.type = 
+				vi.IsRGB32() ? MSP_RGB32 : 
+				vi.IsRGB24() ? MSP_RGB24 : 
+				vi.IsYUY2() ? MSP_YUY2 : 
+				/*vi.IsYV12()*/ vi.pixel_type == VideoInfo::CS_YV12 ? (s_fSwapUV?MSP_IYUV:MSP_YV12) : 
+				/*vi.IsIYUV()*/ vi.pixel_type == VideoInfo::CS_IYUV ? (s_fSwapUV?MSP_YV12:MSP_IYUV) : 
+				-1;
+
+			float fps = m_fps > 0 ? m_fps : (float)vi.fps_numerator / vi.fps_denominator;
+
+			Render(dst, (REFERENCE_TIME)(10000000i64 * n / fps), fps);
+
+			return(frame);
+		}
+	};
+
+	class CVobSubAvisynthFilter : public CVobSubFilter, public CAvisynthFilter
+	{
+	public:
+		CVobSubAvisynthFilter(PClip c, const char* fn, IScriptEnvironment* env)
+			: CVobSubFilter(CString(fn))
+			, CAvisynthFilter(c, env)
+		{
+			if(!m_pSubPicProvider)
+				env->ThrowError("VobSub: Can't open \"%s\"", fn);
+		}
+	};
+
+	AVSValue __cdecl VobSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CVobSubAvisynthFilter(args[0].AsClip(), args[1].AsString(), env));
+	}
+    
+	class CTextSubAvisynthFilter : public CTextSubFilter, public CAvisynthFilter
+	{
+	public:
+		CTextSubAvisynthFilter(PClip c, IScriptEnvironment* env, const char* fn, int CharSet = DEFAULT_CHARSET, float fps = -1)
+			: CTextSubFilter(CString(fn), CharSet, fps)
+			, CAvisynthFilter(c, env)
+		{
+			if(!m_pSubPicProvider)
+				env->ThrowError("TextSub: Can't open \"%s\"", fn);
+		}
+	};
+
+	AVSValue __cdecl TextSubCreateS(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString()));
+	}
+
+	AVSValue __cdecl TextSubCreateSI(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt()));
+	}
+
+	AVSValue __cdecl TextSubCreateSIF(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		return(new CTextSubAvisynthFilter(args[0].AsClip(), env, args[1].AsString(), args[2].AsInt(), args[3].AsFloat()));
+	}
+
+	AVSValue __cdecl TextSubSwapUV(AVSValue args, void* user_data, IScriptEnvironment* env)
+	{
+		s_fSwapUV = args[0].AsBool(false);
+		return(AVSValue());
+	}
+
+	extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* env)
+	{
+		env->AddFunction("VobSub", "cs", VobSubCreateS, 0);
+		env->AddFunction("TextSub", "cs", TextSubCreateS, 0);
+		env->AddFunction("TextSub", "csi", TextSubCreateSI, 0);
+		env->AddFunction("TextSub", "csif", TextSubCreateSIF, 0);
+		env->AddFunction("TextSubSwapUV", "b", TextSubSwapUV, 0);
+		return(NULL);
+	}
+}
+
+}
+
+UINT CALLBACK OpenHookProc(HWND hDlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+	switch(uiMsg)
+	{
+		case WM_NOTIFY:
+		{
+			OPENFILENAME* ofn = ((OFNOTIFY *)lParam)->lpOFN;
+
+			if(((NMHDR *)lParam)->code == CDN_FILEOK)
+			{
+				ofn->lCustData = (LPARAM)CharSetList[SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_GETCURSEL, 0, 0)];
+			}
+
+			break;
+		}
+
+		case WM_INITDIALOG:
+		{
+			SetWindowLong(hDlg, GWL_USERDATA, lParam);
+
+			for(int i = 0; i < CharSetLen; i++)
+			{
+				CString s;
+				s.Format(_T("%s (%d)"), CharSetNames[i], CharSetList[i]);
+				SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_ADDSTRING, 0, (LONG)(LPCTSTR)s);
+				if(CharSetList[i] == (int)((OPENFILENAME*)lParam)->lCustData)
+					SendMessage(GetDlgItem(hDlg, IDC_COMBO1), CB_SETCURSEL, i, 0);
+			}
+
+			break;
+		}
+		
+		default:
+			break;
+	}
+
+	return FALSE;
+}
diff --git a/vsfilter/res/Icon_147.ico b/vsfilter/res/Icon_147.ico
new file mode 100644
index 0000000000000000000000000000000000000000..4414c25de91dadeceffd961d5027edde18f30f19
Binary files /dev/null and b/vsfilter/res/Icon_147.ico differ
diff --git a/vsfilter/res/VSFilter.manifest b/vsfilter/res/VSFilter.manifest
new file mode 100644
index 0000000000000000000000000000000000000000..1c15a6e453397d378582b237805b032d53912770
--- /dev/null
+++ b/vsfilter/res/VSFilter.manifest
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<!--assemblyIdentity
+    version="1.0.0.0"
+    processorArchitecture="X86"
+    name="Gabest.VobSub.Filter"
+    type="win32"
+/-->
+<description>DirectVobSub</description>
+<dependency>
+    <dependentAssembly>
+        <assemblyIdentity
+            type="win32"
+            name="Microsoft.Windows.Common-Controls"
+            version="6.0.0.0"
+            processorArchitecture="X86"
+            publicKeyToken="6595b64144ccf1df"
+            language="*"
+        />
+    </dependentAssembly>
+</dependency>
+</assembly>
+
diff --git a/vsfilter/resource.h b/vsfilter/resource.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2886d2fa02ec694bb5fc3447c0d7609c29807fe
--- /dev/null
+++ b/vsfilter/resource.h
@@ -0,0 +1,183 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by VSFilter.rc
+//
+#define IDS_PROJNAME                    100
+#define IDD_DVSPATHSPAGE                101
+#define IDS_PROJNAME2                   102
+#define IDS_DIVX_WARNING                103
+#define IDD_DVSABOUTPAGE                104
+#define IDS_R_TEXT                      105
+#define IDS_R_VOBSUB                    106
+#define IDS_R_DEFTEXTPATHES             107
+#define IDS_R_PREFLANGS                 108
+#define IDS_RG_SAVEFULLPATH             109
+#define IDS_RG_EXTPIC                   110
+#define IDS_RG_MOD32FIX                 111
+#define IDS_RG_LOADLEVEL                112
+#define IDS_RG_INSTANTUPDATE            113
+#define IDS_RG_FLIPPICTURE              114
+#define IDS_RG_HIDE                     115
+#define IDS_RG_NOYUY2COLORS             116
+#define IDS_RG_DOPREBUFFERING           117
+#define IDS_RG_SHOWOSDSTATS             118
+#define IDS_RG_SEENDIVXWARNING          119
+#define IDS_RG_RESX2                    120
+#define IDS_RG_RESX2MINW                121
+#define IDS_RG_RESX2MINH                122
+#define IDS_RTM_MEDIAFPS                123
+#define IDS_RT_SHADOW                   124
+#define IDS_RT_OUTLINE                  125
+#define IDS_RT_LOGFONT                  126
+#define IDS_RT_COLOR                    127
+#define IDS_RT_OVERRIDEPLACEMENT        128
+#define IDS_RT_XPERC                    129
+#define IDS_RT_YPERC                    130
+#define IDS_RV_BUFFER                   131
+#define IDS_RP_PATH                     132
+#define IDS_RL_LANG                     133
+#define IDS_M_SHOWSUBTITLES             134
+#define IDS_M_HIDESUBTITLES             135
+#define IDS_M_ORIGINALPICTURE           136
+#define IDS_M_FLIPPEDPICTURE            137
+#define IDS_RV_POLYGONIZE               138
+#define IDS_RG_EXTERNALLOAD             139
+#define IDS_RG_WEBLOAD                  140
+#define IDS_RG_EMBEDDEDLOAD             141
+#define IDS_URL_HOMEPAGE                142
+#define IDS_URL_EMAIL                   143
+#define IDS_R_TIMING                    144
+#define IDS_RTM_SUBTITLEDELAY           145
+#define IDS_RTM_SUBTITLESPEEDMUL        146
+#define IDS_RT_ADVANCEDRENDERER         147
+#define IDS_RV_ONLYSHOWFORCEDSUBS       148
+#define IDS_ORGHEIGHT                   149
+#define IDS_RG_VMRZOOMENABLED           150
+#define IDS_RTM_SUBTITLESPEEDDIV        151
+#define IDS_RG_COLORFORMATS             152
+#define IDS_EXTTO169                    153
+#define IDS_RG_FORCERGB                 154
+#define IDS_RG_ENABLEZPICON             155
+#define IDS_RG_FLIPSUBTITLES            156
+#define IDS_RTM_MEDIAFPSENABLED         157
+#define IDS_RG_DISABLERELOADER          158
+#define IDS_EXTTO43                     159
+#define IDS_CROPTO169                   160
+#define IDS_CROPTO43                    161
+#define IDS_ORGRES                      162
+#define IDS_DBLRES                      163
+#define IDS_DBLRESIF                    164
+#define IDS_DONOTLOAD                   165
+#define IDS_LOADWHENNEEDED              166
+#define IDS_ALWAYSLOAD                  167
+#define IDS_EXTTO576                    168
+#define IDS_EXTTO480                    169
+#define IDC_FILENAME                    201
+#define IDD_DVSMAINPAGE                 201
+#define IDC_OPEN                        202
+#define IDC_HOMEPAGEBTN                 202
+#define IDD_DVSZOOMPAGE                 202
+#define IDC_FLIP                        203
+#define IDC_HIDE                        204
+#define IDC_FLIPSUB                     205
+#define IDC_CACHESUB                    206
+#define IDC_ONLYSHOWFORCEDSUBS          207
+#define IDC_POLYGONIZE                  208
+#define IDC_FONT                        212
+#define IDC_BUGREPORTBTN                212
+#define IDC_CONFIGDLG                   213
+#define IDC_SHADOW                      214
+#define IDI_ICON1                       214
+#define IDC_LANGCOMBO                   215
+#define IDD_DVSCOLORPAGE                218
+#define IDC_OUTLINE                     220
+#define IDD_DVSGENERALPAGE              220
+#define IDC_MOD32FIX                    221
+#define IDD_DVSTIMINGPAGE               221
+#define IDD_DVSMISCPAGE                 222
+#define IDC_PREBUFFERING                223
+#define IDS_R_GENERAL                   223
+#define IDC_EMBLOAD                     224
+#define IDS_RT_STYLE                    224
+#define IDC_SHOWOSDSTATS                225
+#define IDC_OVERRIDEPLACEMENT           226
+#define IDC_EDIT1                       228
+#define IDC_EDIT2                       229
+#define IDC_EDIT7                       230
+#define IDC_AUTORELOAD                  230
+#define IDD_TEXTSUBOPENTEMPLATE         230
+#define IDC_SPIN1                       231
+#define IDC_SPIN2                       232
+#define IDC_SPIN7                       233
+#define IDC_MODFPS                      233
+#define IDC_EDIT8                       234
+#define IDC_ADVANCEDRENDERER            234
+#define IDC_SPIN8                       235
+#define IDC_EXTLOAD                     235
+#define IDC_WEBLOAD                     236
+#define IDC_EDIT3                       238
+#define IDC_SPIN3                       239
+#define IDC_EDIT4                       240
+#define IDD_STYLEDIALOG                 240
+#define IDC_SPIN4                       241
+#define IDC_EDIT5                       242
+#define IDC_SPIN5                       243
+#define IDC_EDIT6                       244
+#define IDC_SPIN6                       245
+#define IDC_EDIT9                       246
+#define IDC_SPIN9                       247
+#define IDC_PREFLIST                    257
+#define IDC_DYNCHGLIST                  259
+#define IDC_COLORUP                     261
+#define IDC_COLORDOWN                   262
+#define IDC_COLORCHANGE                 263
+#define IDC_DYNCHGWARNING               266
+#define IDC_DYNCHGWARNING2              268
+#define IDC_FORCERGBCHK                 269
+#define IDC_RESX2COMBO                  290
+#define IDC_VEREXTCOMBO                 298
+#define IDC_LOADCOMBO                   299
+#define IDC_FPS                         300
+#define IDC_SAVEFULLPATH                303
+#define IDC_INSTANTUPDATE               305
+#define IDC_PATHLIST                    307
+#define IDC_ADD                         308
+#define IDC_REMOVE                      309
+#define IDC_BROWSE                      310
+#define IDC_PATHEDIT                    311
+#define IDC_COMBO1                      312
+#define IDC_SLIDER2                     1020
+#define IDC_SLIDER3                     1021
+#define IDC_RADIO1                      1022
+#define IDC_RADIO2                      1023
+#define IDC_CHECK1                      1025
+#define IDC_BUTTON1                     1037
+#define IDC_EDIT10                      1042
+#define IDC_RADIO3                      1044
+#define IDC_RADIO4                      1045
+#define IDC_RADIO5                      1046
+#define IDC_RADIO6                      1047
+#define IDC_SLIDER5                     1048
+#define IDC_SLIDER6                     1049
+#define IDC_EDIT11                      1050
+#define IDC_SPIN10                      1051
+#define IDC_RADIO7                      1056
+#define IDC_RADIO8                      1057
+#define IDC_RADIO9                      1058
+#define IDC_RADIO10                     1059
+#define IDC_RADIO11                     1060
+#define IDC_COLORPRI                    1063
+#define IDC_COLORSEC                    1064
+#define IDC_COLOROUTL                   1065
+#define IDC_COLORSHAD                   1066
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        231
+#define _APS_NEXT_COMMAND_VALUE         32770
+#define _APS_NEXT_CONTROL_VALUE         313
+#define _APS_NEXT_SYMED_VALUE           103
+#endif
+#endif
diff --git a/vsfilter/subpic/CoordGeom.cpp b/vsfilter/subpic/CoordGeom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4118cc99e4694da35b2fec1279dcc7216b949a6d
--- /dev/null
+++ b/vsfilter/subpic/CoordGeom.cpp
@@ -0,0 +1,520 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <math.h>
+#include "CoordGeom.h"
+
+#define EPSILON (1e-7)
+#define BIGNUMBER (1e+9)
+#define IsZero(d) (fabs(d) < EPSILON)
+
+//
+// Vector
+//
+
+Vector::Vector(float x, float y, float z)
+{
+	this->x = x;
+	this->y = y;
+	this->z = z;
+}
+
+void Vector::Set(float x, float y, float z)
+{
+	this->x = x;
+	this->y = y;
+	this->z = z;
+}
+
+float Vector::Length()
+{
+	return(sqrt(x * x + y * y + z * z));
+}
+
+float Vector::Sum()
+{
+	return(x + y + z);
+}
+
+float Vector::CrossSum()
+{
+	return(x*y + x*z + y*z);
+}
+
+Vector Vector::Cross()
+{
+	return(Vector(x*y, x*z, y*z));
+}
+
+Vector Vector::Pow(float exp)
+{
+	return(exp == 0 ? Vector(1, 1, 1) : exp == 1 ? *this : Vector(pow(x, exp), pow(y, exp), pow(z, exp)));
+}
+
+Vector Vector::Unit()
+{
+	float l = Length();
+	if(!l || l == 1) return(*this);
+	return(*this * (1 / l));
+}
+
+Vector& Vector::Unitalize()
+{
+	return(*this = Unit());
+}
+
+Vector Vector::Normal(Vector& a, Vector& b)
+{
+	return((a - *this) % (b - a));
+}
+
+float Vector::Angle(Vector& a, Vector& b)
+{
+	return(((a - *this).Unit()).Angle((b - *this).Unit()));
+}
+
+float Vector::Angle(Vector& a)
+{
+	float angle = *this | a;
+	return((angle > 1) ? 0 : (angle < -1) ? PI : acos(angle));
+}
+
+void Vector::Angle(float& u, float& v)
+{
+	Vector n = Unit();
+
+	u = asin(n.y);
+
+	if(IsZero(n.z)) v = PI/2 * Sgn(n.x);
+	else if(n.z > 0) v = atan(n.x / n.z);
+	else if(n.z < 0) v = IsZero(n.x) ? PI : (PI * Sgn(n.x) + atan(n.x / n.z));
+}
+
+Vector Vector::Angle()
+{
+	Vector ret;
+	Angle(ret.x, ret.y);
+	ret.z = 0;
+	return(ret);
+}
+
+Vector& Vector::Min(Vector& a)
+{
+	x = (x < a.x) ? x : a.x;
+	y = (y < a.y) ? y : a.y;
+	z = (z < a.z) ? z : a.z;
+	return(*this);
+}
+
+Vector& Vector::Max(Vector& a)
+{
+	x = (x > a.x) ? x : a.x;
+	y = (y > a.y) ? y : a.y;
+	z = (z > a.z) ? z : a.z;
+	return(*this);
+}
+
+Vector Vector::Abs()
+{
+	return(Vector(fabs(x), fabs(y), fabs(z)));
+}
+
+Vector Vector::Reflect(Vector& n)
+{
+	return(n * ((-*this) | n) * 2 - (-*this));
+}
+
+Vector Vector::Refract(Vector& N, float nFront, float nBack, float* nOut)
+{
+	Vector D = -*this;
+
+	float N_dot_D = (N | D);
+	float n = N_dot_D >= 0 ? (nFront / nBack) : (nBack / nFront);
+
+	Vector cos_D = N * N_dot_D;
+	Vector sin_T = (cos_D - D) * n;
+
+	float len_sin_T = sin_T | sin_T;
+
+	if(len_sin_T > 1) 
+	{
+		if(nOut) {*nOut = N_dot_D >= 0 ? nFront : nBack;}
+		return((*this).Reflect(N));
+	}
+
+	float N_dot_T = sqrt(1.0 - len_sin_T);
+	if(N_dot_D < 0) N_dot_T = -N_dot_T;
+
+	if(nOut) {*nOut = N_dot_D >= 0 ? nBack : nFront;}
+
+	return(sin_T - (N * N_dot_T));
+}
+
+Vector Vector::Refract2(Vector& N, float nFrom, float nTo, float* nOut)
+{
+	Vector D = -*this;
+
+	float N_dot_D = (N | D);
+	float n = nFrom / nTo;
+
+	Vector cos_D = N * N_dot_D;
+	Vector sin_T = (cos_D - D) * n;
+
+	float len_sin_T = sin_T | sin_T;
+
+	if(len_sin_T > 1) 
+	{
+		if(nOut) {*nOut = nFrom;}
+		return((*this).Reflect(N));
+	}
+
+	float N_dot_T = sqrt(1.0 - len_sin_T);
+	if(N_dot_D < 0) N_dot_T = -N_dot_T;
+
+	if(nOut) {*nOut = nTo;}
+
+	return(sin_T - (N * N_dot_T));
+}
+
+float Vector::operator | (Vector& v)
+{
+	return(x * v.x + y * v.y + z * v.z);
+}
+
+Vector Vector::operator % (Vector& v)
+{
+	return(Vector(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x));
+}
+
+float& Vector::operator [] (int i)
+{
+	return(!i ? x : (i == 1) ? y : z);
+}
+
+Vector Vector::operator - ()
+{
+	return(Vector(-x, -y, -z));
+}
+
+bool Vector::operator == (const Vector& v) const
+{
+	if(IsZero(x - v.x) && IsZero(y - v.y) && IsZero(z - v.z)) return(true);
+	return(false);
+}
+
+bool Vector::operator != (const Vector& v) const
+{
+	return((*this == v) ? false : true);
+}
+
+Vector Vector::operator + (float d)
+{
+	return(Vector(x + d, y + d, z + d));
+}
+
+Vector Vector::operator + (Vector& v)
+{
+	return(Vector(x + v.x, y + v.y, z + v.z));
+}
+
+Vector Vector::operator - (float d)
+{
+	return(Vector(x - d, y - d, z - d));
+}
+
+Vector Vector::operator - (Vector& v)
+{
+	return(Vector(x - v.x, y - v.y, z - v.z));
+}
+
+Vector Vector::operator * (float d)
+{
+	return(Vector(x * d, y * d, z * d));
+}
+
+Vector Vector::operator * (Vector& v)
+{
+	return(Vector(x * v.x, y * v.y, z * v.z));
+}
+
+Vector Vector::operator / (float d)
+{
+	return(Vector(x / d, y / d, z / d));
+}
+
+Vector Vector::operator / (Vector& v)
+{
+	return(Vector(x / v.x, y / v.y, z / v.z));
+}
+
+Vector& Vector::operator += (float d)
+{
+	x += d; y += d; z += d;
+	return(*this);
+}
+
+Vector& Vector::operator += (Vector& v)
+{
+	x += v.x; y += v.y; z += v.z;
+	return(*this);
+}
+
+Vector& Vector::operator -= (float d)
+{
+	x -= d; y -= d; z -= d;
+	return(*this);
+}
+
+Vector& Vector::operator -= (Vector& v)
+{
+	x -= v.x; y -= v.y; z -= v.z;
+	return(*this);
+}
+
+Vector& Vector::operator *= (float d)
+{
+	x *= d; y *= d; z *= d;
+	return(*this);
+}
+
+Vector& Vector::operator *= (Vector& v)
+{
+	x *= v.x; y *= v.y; z *= v.z;
+	return(*this);
+}
+
+Vector& Vector::operator /= (float d)
+{
+	x /= d; y /= d; z /= d;
+	return(*this);
+}
+
+Vector& Vector::operator /= (Vector& v)
+{
+	x /= v.x; y /= v.y; z /= v.z;
+	return(*this);
+}
+
+//
+// Ray
+//
+
+Ray::Ray(Vector& p, Vector& d)
+{
+	this->p = p;
+	this->d = d;
+}
+
+void Ray::Set(Vector& p, Vector& d)
+{
+	this->p = p;
+	this->d = d;
+}
+
+float Ray::GetDistanceFrom(Ray& r)
+{
+	float t = (d | r.d);
+	if(IsZero(t)) return(-BIGNUMBER); // plane is paralell to the ray, return -infinite
+	return(((r.p - p) | r.d) / t);
+}
+
+float Ray::GetDistanceFrom(Vector& v)
+{
+	float t = ((v - p) | d) / (d | d);
+	return(((p + d*t) - v).Length());
+}
+
+Vector Ray::operator [] (float t)
+{
+	return(p + d*t);
+}
+
+//
+// XForm
+//
+
+XForm::XForm(Ray& r, Vector& s, bool isWorldToLocal)
+{
+	Initalize(r, s, isWorldToLocal);
+}
+
+void XForm::Initalize()
+{
+	m.Initalize();
+}
+
+void XForm::Initalize(Ray& r, Vector& s, bool isWorldToLocal)
+{
+	Initalize();
+
+	if(m_isWorldToLocal = isWorldToLocal)
+	{
+		*this -= r.p;
+		*this >>= r.d;
+		*this /= s;
+
+	}
+	else
+	{
+		*this *= s;
+		*this <<= r.d;
+		*this += r.p;
+	}
+}
+
+void XForm::operator *= (Vector& v)
+{
+	Matrix s;
+	s.mat[0][0] = v.x;
+	s.mat[1][1] = v.y;
+	s.mat[2][2] = v.z;
+	m *= s;
+}
+
+void XForm::operator += (Vector& v)
+{
+	Matrix t;
+	t.mat[3][0] = v.x;
+	t.mat[3][1] = v.y;
+	t.mat[3][2] = v.z;
+	m *= t;
+}
+
+void XForm::operator <<= (Vector& v)
+{
+	Matrix x;
+	x.mat[1][1] = cos(v.x); x.mat[1][2] = -sin(v.x);
+	x.mat[2][1] = sin(v.x); x.mat[2][2] = cos(v.x);
+
+	Matrix y;
+	y.mat[0][0] = cos(v.y); y.mat[0][2] = -sin(v.y);
+	y.mat[2][0] = sin(v.y); y.mat[2][2] = cos(v.y);
+
+	Matrix z;
+	z.mat[0][0] = cos(v.z); z.mat[0][1] = -sin(v.z);
+	z.mat[1][0] = sin(v.z); z.mat[1][1] = cos(v.z);
+
+	m = m_isWorldToLocal ? (m * y * x * z) : (m * z * x * y);
+}
+
+void XForm::operator /= (Vector& v)
+{
+	Vector s;
+	s.x = IsZero(v.x) ? 0 : 1 / v.x;
+	s.y = IsZero(v.y) ? 0 : 1 / v.y;
+	s.z = IsZero(v.z) ? 0 : 1 / v.z;
+	*this *= s;
+}
+
+void XForm::operator -= (Vector& v)
+{
+	*this += -v;
+}
+
+void XForm::operator >>= (Vector& v)
+{
+	*this <<= -v;
+}
+
+Vector XForm::operator < (Vector& n)
+{
+	Vector ret;
+
+	ret.x = n.x * m.mat[0][0] +
+			n.y * m.mat[1][0] +
+			n.z * m.mat[2][0];
+	ret.y = n.x * m.mat[0][1] +
+			n.y * m.mat[1][1] +
+			n.z * m.mat[2][1];
+	ret.z = n.x * m.mat[0][2] +
+			n.y * m.mat[1][2] +
+			n.z * m.mat[2][2];
+
+	return(ret);
+}
+
+Vector XForm::operator << (Vector& v)
+{
+	Vector ret;
+
+	ret.x = v.x * m.mat[0][0] +
+			v.y * m.mat[1][0] +
+			v.z * m.mat[2][0] +
+			m.mat[3][0];
+	ret.y = v.x * m.mat[0][1] +
+			v.y * m.mat[1][1] +
+			v.z * m.mat[2][1] +
+			m.mat[3][1];
+	ret.z = v.x * m.mat[0][2] +
+			v.y * m.mat[1][2] +
+			v.z * m.mat[2][2] +
+			m.mat[3][2];
+
+	return(ret);
+}
+
+Ray XForm::operator << (Ray& r)
+{
+	return(Ray(*this << r.p, *this < r.d));
+}
+
+//
+// XForm::Matrix
+//
+
+XForm::Matrix::Matrix()
+{
+	Initalize();
+}
+
+void XForm::Matrix::Initalize()
+{
+	mat[0][0] = 1; mat[0][1] = 0; mat[0][2] = 0; mat[0][3] = 0;
+	mat[1][0] = 0; mat[1][1] = 1; mat[1][2] = 0; mat[1][3] = 0;
+	mat[2][0] = 0; mat[2][1] = 0; mat[2][2] = 1; mat[2][3] = 0;
+	mat[3][0] = 0; mat[3][1] = 0; mat[3][2] = 0; mat[3][3] = 1;
+}
+
+XForm::Matrix XForm::Matrix::operator * (Matrix& m)
+{
+	Matrix ret;
+
+	for(int i = 0; i < 4; i++)
+	{
+		for(int j = 0; j < 4; j++)
+		{
+			ret.mat[i][j] = mat[i][0] * m.mat[0][j] +
+							mat[i][1] * m.mat[1][j] +
+							mat[i][2] * m.mat[2][j] +
+							mat[i][3] * m.mat[3][j];
+
+			if(IsZero(ret.mat[i][j])) ret.mat[i][j] = 0;
+		}
+	}
+
+	return(ret);
+}
+
+XForm::Matrix& XForm::Matrix::operator *= (XForm::Matrix& m)
+{
+	return(*this = *this * m);
+}
diff --git a/vsfilter/subpic/CoordGeom.h b/vsfilter/subpic/CoordGeom.h
new file mode 100644
index 0000000000000000000000000000000000000000..81ad0dff5ecc1606778f4cb87bb18fe29369513a
--- /dev/null
+++ b/vsfilter/subpic/CoordGeom.h
@@ -0,0 +1,141 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once 
+
+#ifndef PI
+#define PI (3.141592654f)
+#endif
+
+#define DegToRad(d) ((d)*PI/180.0)
+#define RadToDeg(r) ((r)*180.0/PI)
+#define Sgn(d) (IsZero(d) ? 0 : (d) > 0 ? 1 : -1)
+#define SgnPow(d, p) (IsZero(d) ? 0 : (pow(fabs(d), p) * Sgn(d)))
+
+class Vector
+{
+public:
+	float x, y, z;
+
+	Vector() {x = y = z = 0;}
+	Vector(float x, float y, float z);
+	void Set(float x, float y, float z);
+
+	Vector Normal(Vector& a, Vector& b);
+	float Angle(Vector& a, Vector& b);
+	float Angle(Vector& a);
+	void Angle(float& u, float& v); // returns spherical coords in radian, -PI/2 <= u <= PI/2, -PI <= v <= PI
+	Vector Angle(); // does like prev., returns 'u' in 'ret.x', and 'v' in 'ret.y'
+
+	Vector Unit();
+	Vector& Unitalize();
+	float Length();
+	float Sum(); // x + y + z
+	float CrossSum(); // xy + xz + yz
+	Vector Cross(); // xy, xz, yz
+	Vector Pow(float exp);
+
+	Vector& Min(Vector& a);
+	Vector& Max(Vector& a);
+	Vector Abs();
+
+	Vector Reflect(Vector& n);
+	Vector Refract(Vector& n, float nFront, float nBack, float* nOut = NULL);
+	Vector Refract2(Vector& n, float nFrom, float nTo, float* nOut = NULL);
+
+	Vector operator - ();
+	float& operator [] (int i);
+
+	float operator | (Vector& v); // dot
+	Vector operator % (Vector& v); // cross
+
+	bool operator == (const Vector& v) const;
+	bool operator != (const Vector& v) const;
+
+	Vector operator + (float d);
+	Vector operator + (Vector& v);
+	Vector operator - (float d);
+	Vector operator - (Vector& v);
+	Vector operator * (float d);
+	Vector operator * (Vector& v);
+	Vector operator / (float d);
+	Vector operator / (Vector& v);
+	Vector& operator += (float d);
+	Vector& operator += (Vector& v);
+	Vector& operator -= (float d);
+	Vector& operator -= (Vector& v);
+	Vector& operator *= (float d);
+	Vector& operator *= (Vector& v);
+	Vector& operator /= (float d);
+	Vector& operator /= (Vector& v);
+};
+
+class Ray
+{
+public:
+	Vector p, d;
+
+	Ray() {}
+	Ray(Vector& p, Vector& d);
+	void Set(Vector& p, Vector& d);
+
+	float GetDistanceFrom(Ray& r); // r = plane
+	float GetDistanceFrom(Vector& v); // v = point
+
+	Vector operator [] (float t);
+};
+
+class XForm
+{
+	class Matrix
+	{
+	public:
+		float mat[4][4];
+
+		Matrix();
+		void Initalize();
+
+		Matrix operator * (Matrix& m);
+		Matrix& operator *= (Matrix& m);
+	} m;
+
+	bool m_isWorldToLocal;
+
+public:
+	XForm() {}
+	XForm(Ray& r, Vector& s, bool isWorldToLocal = true);
+
+	void Initalize();
+	void Initalize(Ray& r, Vector& s, bool isWorldToLocal = true);
+
+	void operator *= (Vector& s); // scale
+	void operator += (Vector& t); // translate
+	void operator <<= (Vector& r); // rotate
+
+	void operator /= (Vector& s); // scale
+	void operator -= (Vector& t); // translate
+	void operator >>= (Vector& r); // rotate
+
+//	transformations
+	Vector operator < (Vector& n); // normal
+	Vector operator << (Vector& v); // vector
+	Ray operator << (Ray& r); // ray
+};
diff --git a/vsfilter/subpic/DX7SubPic.cpp b/vsfilter/subpic/DX7SubPic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..089df8bf43739c27e38acde66c6f65f785d8fe30
--- /dev/null
+++ b/vsfilter/subpic/DX7SubPic.cpp
@@ -0,0 +1,293 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <ddraw.h>
+#include <d3d.h>
+#include "DX7SubPic.h"
+
+//
+// CDX7SubPic
+//
+
+CDX7SubPic::CDX7SubPic(IDirect3DDevice7* pD3DDev, IDirectDrawSurface7* pSurface)
+	: m_pSurface(pSurface)
+	, m_pD3DDev(pD3DDev)
+{
+	DDSURFACEDESC2 ddsd;
+	INITDDSTRUCT(ddsd);
+	if(SUCCEEDED(m_pSurface->GetSurfaceDesc(&ddsd)))
+	{
+		m_maxsize.SetSize(ddsd.dwWidth, ddsd.dwHeight);
+		m_rcDirty.SetRect(0, 0, ddsd.dwWidth, ddsd.dwHeight);
+	}
+}
+
+// ISubPic
+
+STDMETHODIMP_(void*) CDX7SubPic::GetObject()
+{
+	return (void*)(IDirectDrawSurface7*)m_pSurface;
+}
+
+STDMETHODIMP CDX7SubPic::GetDesc(SubPicDesc& spd)
+{
+	DDSURFACEDESC2 ddsd;
+	INITDDSTRUCT(ddsd);
+	if(FAILED(m_pSurface->GetSurfaceDesc(&ddsd)))
+		return E_FAIL;
+
+	spd.type = 0;
+	spd.w = m_size.cx;
+	spd.h = m_size.cy;
+	spd.bpp = (WORD)ddsd.ddpfPixelFormat.dwRGBBitCount;
+	spd.pitch = ddsd.lPitch;
+	spd.bits = ddsd.lpSurface; // should be NULL
+	spd.vidrect = m_vidrect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX7SubPic::CopyTo(ISubPic* pSubPic)
+{
+	HRESULT hr;
+	if(FAILED(hr = __super::CopyTo(pSubPic)))
+		return hr;
+
+	CPoint p = m_rcDirty.TopLeft();
+	hr = m_pD3DDev->Load((IDirectDrawSurface7*)pSubPic->GetObject(), &p, (IDirectDrawSurface7*)GetObject(), m_rcDirty, 0);
+
+	return SUCCEEDED(hr) ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP CDX7SubPic::ClearDirtyRect(DWORD color)
+{
+	if(m_rcDirty.IsRectEmpty())
+		return S_FALSE;
+
+	DDBLTFX fx;
+	INITDDSTRUCT(fx);
+	fx.dwFillColor = color;
+	m_pSurface->Blt(&m_rcDirty, NULL, NULL, DDBLT_WAIT|DDBLT_COLORFILL, &fx);
+	
+	m_rcDirty.SetRectEmpty();
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX7SubPic::Lock(SubPicDesc& spd)
+{
+	DDSURFACEDESC2 ddsd;
+	INITDDSTRUCT(ddsd);
+	if(FAILED(m_pSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT, NULL)))
+		return E_FAIL;
+
+	spd.type = 0;
+	spd.w = m_size.cx;
+	spd.h = m_size.cy;
+	spd.bpp = (WORD)ddsd.ddpfPixelFormat.dwRGBBitCount;
+	spd.pitch = ddsd.lPitch;
+	spd.bits = ddsd.lpSurface;
+	spd.vidrect = m_vidrect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX7SubPic::Unlock(RECT* pDirtyRect)
+{
+	m_pSurface->Unlock(NULL);
+
+	if(pDirtyRect)
+	{
+		m_rcDirty = *pDirtyRect;
+		m_rcDirty.InflateRect(1, 1);
+		m_rcDirty &= CRect(CPoint(0, 0), m_size);
+	}
+	else
+	{
+		m_rcDirty = CRect(CPoint(0, 0), m_size);
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX7SubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget)
+{
+	ASSERT(pTarget == NULL);
+
+	if(!m_pD3DDev || !m_pSurface || !pSrc || !pDst)
+		return E_POINTER;
+
+	CRect src(*pSrc), dst(*pDst);
+
+	HRESULT hr;
+
+    do
+	{
+		DDSURFACEDESC2 ddsd;
+		INITDDSTRUCT(ddsd);
+		if(FAILED(hr = m_pSurface->GetSurfaceDesc(&ddsd)))
+			break;
+
+        float w = (float)ddsd.dwWidth;
+        float h = (float)ddsd.dwHeight;
+
+		struct
+		{
+			float x, y, z, rhw;
+			float tu, tv;
+		}
+		pVertices[] =
+		{
+			{(float)dst.left, (float)dst.top, 0.5f, 2.0f, (float)src.left / w, (float)src.top / h},
+			{(float)dst.right, (float)dst.top, 0.5f, 2.0f, (float)src.right / w, (float)src.top / h},
+			{(float)dst.left, (float)dst.bottom, 0.5f, 2.0f, (float)src.left / w, (float)src.bottom / h},
+			{(float)dst.right, (float)dst.bottom, 0.5f, 2.0f, (float)src.right / w, (float)src.bottom / h},
+		};
+/*
+		for(int i = 0; i < countof(pVertices); i++)
+		{
+			pVertices[i].x -= 0.5;
+			pVertices[i].y -= 0.5;
+		}
+*/
+        hr = m_pD3DDev->SetTexture(0, m_pSurface);
+
+        m_pD3DDev->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
+        m_pD3DDev->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE);
+        m_pD3DDev->SetRenderState(D3DRENDERSTATE_BLENDENABLE, TRUE);
+        m_pD3DDev->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE); // pre-multiplied src and ...
+        m_pD3DDev->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_SRCALPHA); // ... inverse alpha channel for dst
+
+		m_pD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTFG_LINEAR);
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFN_LINEAR);
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_LINEAR);
+
+        m_pD3DDev->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_CLAMP);
+
+		/*//
+
+		D3DDEVICEDESC7 d3ddevdesc;
+		m_pD3DDev->GetCaps(&d3ddevdesc);
+		if(d3ddevdesc.dpcTriCaps.dwAlphaCmpCaps & D3DPCMPCAPS_LESS)
+		{
+			m_pD3DDev->SetRenderState(D3DRENDERSTATE_ALPHAREF, (DWORD)0x000000FE);
+			m_pD3DDev->SetRenderState(D3DRENDERSTATE_ALPHATESTENABLE, TRUE); 
+			m_pD3DDev->SetRenderState(D3DRENDERSTATE_ALPHAFUNC, D3DPCMPCAPS_LESS);
+		}
+
+        *///
+
+        if(FAILED(hr = m_pD3DDev->BeginScene()))
+			break;
+        
+		hr = m_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP,
+										D3DFVF_XYZRHW | D3DFVF_TEX1,
+										pVertices, 4, D3DDP_WAIT);
+		m_pD3DDev->EndScene();
+
+        //
+
+		m_pD3DDev->SetTexture(0, NULL);
+
+		return S_OK;
+    }
+	while(0);
+
+    return E_FAIL;
+}
+
+//
+// CDX7SubPicAllocator
+//
+
+CDX7SubPicAllocator::CDX7SubPicAllocator(IDirect3DDevice7* pD3DDev, SIZE maxsize, bool fPow2Textures) 
+	: ISubPicAllocatorImpl(maxsize, true, fPow2Textures)
+	, m_pD3DDev(pD3DDev)
+	, m_maxsize(maxsize)
+{
+}
+
+// ISubPicAllocator
+
+STDMETHODIMP CDX7SubPicAllocator::ChangeDevice(IUnknown* pDev)
+{
+	CComQIPtr<IDirect3DDevice7, &IID_IDirect3DDevice7> pD3DDev = pDev;
+	if(!pD3DDev) return E_NOINTERFACE;
+
+	CAutoLock cAutoLock(this);
+	m_pD3DDev = pD3DDev;
+
+	return __super::ChangeDevice(pDev);
+}
+
+// ISubPicAllocatorImpl
+
+bool CDX7SubPicAllocator::Alloc(bool fStatic, ISubPic** ppSubPic)
+{
+	if(!ppSubPic) 
+		return(false);
+
+	CAutoLock cAutoLock(this);
+
+	DDSURFACEDESC2 ddsd;
+	INITDDSTRUCT(ddsd);
+	ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT|DDSD_PIXELFORMAT;
+	ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | (fStatic ? DDSCAPS_SYSTEMMEMORY : 0);
+	ddsd.ddsCaps.dwCaps2 = fStatic ? 0 : (DDSCAPS2_TEXTUREMANAGE|DDSCAPS2_HINTSTATIC);
+	ddsd.dwWidth = m_maxsize.cx;
+	ddsd.dwHeight = m_maxsize.cy;
+	ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
+	ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB|DDPF_ALPHAPIXELS;
+	ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
+	ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000;
+	ddsd.ddpfPixelFormat.dwRBitMask        = 0x00FF0000;
+	ddsd.ddpfPixelFormat.dwGBitMask        = 0x0000FF00;
+	ddsd.ddpfPixelFormat.dwBBitMask        = 0x000000FF;
+
+	if(m_fPow2Textures)
+	{
+		ddsd.dwWidth = ddsd.dwHeight = 1;
+		while(ddsd.dwWidth < m_maxsize.cx) ddsd.dwWidth <<= 1;
+		while(ddsd.dwHeight < m_maxsize.cy) ddsd.dwHeight <<= 1;
+	}
+
+
+	CComPtr<IDirect3D7> pD3D;
+	CComQIPtr<IDirectDraw7, &IID_IDirectDraw7> pDD;
+	if(FAILED(m_pD3DDev->GetDirect3D(&pD3D)) || !pD3D || !(pDD = pD3D))
+		return(false);
+
+	CComPtr<IDirectDrawSurface7> pSurface;
+	if(FAILED(pDD->CreateSurface(&ddsd, &pSurface, NULL)))
+		return(false);
+
+	if(!(*ppSubPic = new CDX7SubPic(m_pD3DDev, pSurface)))
+		return(false);
+
+	(*ppSubPic)->AddRef();
+
+	return(true);
+}
diff --git a/vsfilter/subpic/DX7SubPic.h b/vsfilter/subpic/DX7SubPic.h
new file mode 100644
index 0000000000000000000000000000000000000000..b13dd3d412d31472fa940b424eb89d642a5623aa
--- /dev/null
+++ b/vsfilter/subpic/DX7SubPic.h
@@ -0,0 +1,62 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "ISubPic.h"
+
+// CDX7SubPic
+
+class CDX7SubPic : public ISubPicImpl
+{
+	CComPtr<IDirect3DDevice7> m_pD3DDev;
+	CComPtr<IDirectDrawSurface7> m_pSurface;
+
+protected:
+	STDMETHODIMP_(void*) GetObject(); // returns IDirectDrawSurface7*
+
+public:
+	CDX7SubPic(IDirect3DDevice7* pD3DDev, IDirectDrawSurface7* pSurface);
+
+	// ISubPic
+	STDMETHODIMP GetDesc(SubPicDesc& spd);
+	STDMETHODIMP CopyTo(ISubPic* pSubPic);
+	STDMETHODIMP ClearDirtyRect(DWORD color);
+	STDMETHODIMP Lock(SubPicDesc& spd);
+	STDMETHODIMP Unlock(RECT* pDirtyRect);
+	STDMETHODIMP AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget);
+};
+
+// CDX7SubPicAllocator
+
+class CDX7SubPicAllocator : public ISubPicAllocatorImpl, public CCritSec
+{
+    CComPtr<IDirect3DDevice7> m_pD3DDev;
+	CSize m_maxsize;
+
+	bool Alloc(bool fStatic, ISubPic** ppSubPic);
+
+public:
+	CDX7SubPicAllocator(IDirect3DDevice7* pD3DDev, SIZE maxsize, bool fPow2Textures);
+
+	// ISubPicAllocator
+	STDMETHODIMP ChangeDevice(IUnknown* pDev);
+};
diff --git a/vsfilter/subpic/DX9SubPic.cpp b/vsfilter/subpic/DX9SubPic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e69d4f0aa66c5d7862aa9ea6b39011bd0d4c57ac
--- /dev/null
+++ b/vsfilter/subpic/DX9SubPic.cpp
@@ -0,0 +1,361 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <d3d9.h>
+#include <Vmr9.h>
+#include "DX9SubPic.h"
+
+//
+// CDX9SubPic
+//
+
+CDX9SubPic::CDX9SubPic(IDirect3DSurface9* pSurface)
+	: m_pSurface(pSurface)
+{
+	D3DSURFACE_DESC d3dsd;
+	ZeroMemory(&d3dsd, sizeof(d3dsd));
+	if(SUCCEEDED(m_pSurface->GetDesc(&d3dsd)))
+	{
+		m_maxsize.SetSize(d3dsd.Width, d3dsd.Height);
+		m_rcDirty.SetRect(0, 0, d3dsd.Width, d3dsd.Height);
+	}
+}
+
+// ISubPic
+
+STDMETHODIMP_(void*) CDX9SubPic::GetObject()
+{
+	CComPtr<IDirect3DTexture9> pTexture;
+	if(SUCCEEDED(m_pSurface->GetContainer(IID_IDirect3DTexture9, (void**)&pTexture)))
+		return (void*)(IDirect3DTexture9*)pTexture;
+
+	return NULL;
+}
+
+STDMETHODIMP CDX9SubPic::GetDesc(SubPicDesc& spd)
+{
+	D3DSURFACE_DESC d3dsd;
+	ZeroMemory(&d3dsd, sizeof(d3dsd));
+	if(FAILED(m_pSurface->GetDesc(&d3dsd)))
+		return E_FAIL;
+
+	spd.type = 0;
+	spd.w = m_size.cx;
+	spd.h = m_size.cy;
+	spd.bpp = 
+		d3dsd.Format == D3DFMT_A8R8G8B8 ? 32 : 
+		d3dsd.Format == D3DFMT_A4R4G4B4 ? 16 : 0;
+	spd.pitch = 0;
+	spd.bits = NULL;
+	spd.vidrect = m_vidrect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX9SubPic::CopyTo(ISubPic* pSubPic)
+{
+	HRESULT hr;
+	if(FAILED(hr = __super::CopyTo(pSubPic)))
+		return hr;
+
+	if(m_rcDirty.IsRectEmpty())
+		return S_FALSE;
+
+	CComPtr<IDirect3DDevice9> pD3DDev;
+	if(!m_pSurface || FAILED(m_pSurface->GetDevice(&pD3DDev)) || !pD3DDev)
+		return E_FAIL;
+
+	hr = pD3DDev->UpdateTexture((IDirect3DTexture9*)GetObject(), (IDirect3DTexture9*)pSubPic->GetObject());
+
+	return SUCCEEDED(hr) ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP CDX9SubPic::ClearDirtyRect(DWORD color)
+{
+	if(m_rcDirty.IsRectEmpty())
+		return S_FALSE;
+
+	CComPtr<IDirect3DDevice9> pD3DDev;
+	if(!m_pSurface || FAILED(m_pSurface->GetDevice(&pD3DDev)) || !pD3DDev)
+		return E_FAIL;
+
+	SubPicDesc spd;
+	if(SUCCEEDED(Lock(spd)))
+	{
+		int h = m_rcDirty.Height();
+
+		BYTE* ptr = (BYTE*)spd.bits + spd.pitch*m_rcDirty.top + (m_rcDirty.left*spd.bpp>>3);
+
+		if(spd.bpp == 16)
+		{
+			while(h-- > 0)
+			{
+				WORD* start = (WORD*)ptr;
+				WORD* end = start + m_rcDirty.Width();
+				while(start < end) *start++ = (WORD)color;
+				ptr += spd.pitch;
+			}
+		}
+		else if(spd.bpp == 32)
+		{
+			while(h-- > 0)
+			{
+				DWORD* start = (DWORD*)ptr;
+				DWORD* end = start + m_rcDirty.Width();
+				while(start < end) *start++ = color;
+				ptr += spd.pitch;
+			}
+		}
+/*
+		DWORD* ptr = (DWORD*)bm.bits;
+		DWORD* end = ptr + bm.h*bm.wBytes/4;
+		while(ptr < end) *ptr++ = color;
+*/
+		Unlock(NULL);
+	}
+
+//		HRESULT hr = pD3DDev->ColorFill(m_pSurface, m_rcDirty, color);
+	
+	m_rcDirty.SetRectEmpty();
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX9SubPic::Lock(SubPicDesc& spd)
+{
+	D3DSURFACE_DESC d3dsd;
+	ZeroMemory(&d3dsd, sizeof(d3dsd));
+	if(FAILED(m_pSurface->GetDesc(&d3dsd)))
+		return E_FAIL;
+
+	D3DLOCKED_RECT LockedRect;
+	ZeroMemory(&LockedRect, sizeof(LockedRect));
+	if(FAILED(m_pSurface->LockRect(&LockedRect, NULL, 0)))
+		return E_FAIL;
+
+	spd.type = 0;
+	spd.w = m_size.cx;
+	spd.h = m_size.cy;
+	spd.bpp = 
+		d3dsd.Format == D3DFMT_A8R8G8B8 ? 32 : 
+		d3dsd.Format == D3DFMT_A4R4G4B4 ? 16 : 0;
+	spd.pitch = LockedRect.Pitch;
+	spd.bits = LockedRect.pBits;
+	spd.vidrect = m_vidrect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX9SubPic::Unlock(RECT* pDirtyRect)
+{
+	m_pSurface->UnlockRect();
+
+	if(pDirtyRect)
+	{
+		m_rcDirty = *pDirtyRect;
+		m_rcDirty.InflateRect(1, 1);
+		m_rcDirty.left &= ~127;
+		m_rcDirty.top &= ~63;
+		m_rcDirty.right = (m_rcDirty.right + 127) & ~127;
+		m_rcDirty.bottom = (m_rcDirty.bottom + 63) & ~63;
+		m_rcDirty &= CRect(CPoint(0, 0), m_size);
+	}
+	else
+	{
+		m_rcDirty = CRect(CPoint(0, 0), m_size);
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP CDX9SubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget)
+{
+	ASSERT(pTarget == NULL);
+
+	if(!pSrc || !pDst)
+		return E_POINTER;
+
+	CRect src(*pSrc), dst(*pDst);
+
+	CComPtr<IDirect3DDevice9> pD3DDev;
+	CComPtr<IDirect3DTexture9> pTexture = (IDirect3DTexture9*)GetObject();
+	if(!pTexture || FAILED(pTexture->GetDevice(&pD3DDev)) || !pD3DDev)
+		return E_NOINTERFACE;
+
+	HRESULT hr;
+
+    do
+	{
+		D3DSURFACE_DESC d3dsd;
+		ZeroMemory(&d3dsd, sizeof(d3dsd));
+		if(FAILED(pTexture->GetLevelDesc(0, &d3dsd)) /*|| d3dsd.Type != D3DRTYPE_TEXTURE*/)
+			break;
+
+        float w = (float)d3dsd.Width;
+        float h = (float)d3dsd.Height;
+
+		struct
+		{
+			float x, y, z, rhw;
+			float tu, tv;
+		}
+		pVertices[] =
+		{
+			{(float)dst.left, (float)dst.top, 0.5f, 2.0f, (float)src.left / w, (float)src.top / h},
+			{(float)dst.right, (float)dst.top, 0.5f, 2.0f, (float)src.right / w, (float)src.top / h},
+			{(float)dst.left, (float)dst.bottom, 0.5f, 2.0f, (float)src.left / w, (float)src.bottom / h},
+			{(float)dst.right, (float)dst.bottom, 0.5f, 2.0f, (float)src.right / w, (float)src.bottom / h},
+		};
+/*
+		for(int i = 0; i < countof(pVertices); i++)
+		{
+			pVertices[i].x -= 0.5;
+			pVertices[i].y -= 0.5;
+		}
+*/
+
+        hr = pD3DDev->SetTexture(0, pTexture);
+
+		DWORD abe, sb, db;
+		hr = pD3DDev->GetRenderState(D3DRS_ALPHABLENDENABLE, &abe);
+		hr = pD3DDev->GetRenderState(D3DRS_SRCBLEND, &sb);
+		hr = pD3DDev->GetRenderState(D3DRS_DESTBLEND, &db);
+
+        hr = pD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+        hr = pD3DDev->SetRenderState(D3DRS_LIGHTING, FALSE);
+		hr = pD3DDev->SetRenderState(D3DRS_ZENABLE, FALSE);
+    	hr = pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
+        hr = pD3DDev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); // pre-multiplied src and ...
+        hr = pD3DDev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA); // ... inverse alpha channel for dst
+
+		hr = pD3DDev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+        hr = pD3DDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+        hr = pD3DDev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+
+        hr = pD3DDev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+        hr = pD3DDev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+        hr = pD3DDev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+		hr = pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
+		hr = pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
+
+		/*//
+
+		D3DCAPS9 d3dcaps9;
+		hr = pD3DDev->GetDeviceCaps(&d3dcaps9);
+		if(d3dcaps9.AlphaCmpCaps & D3DPCMPCAPS_LESS)
+		{
+			hr = pD3DDev->SetRenderState(D3DRS_ALPHAREF, (DWORD)0x000000FE);
+			hr = pD3DDev->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); 
+			hr = pD3DDev->SetRenderState(D3DRS_ALPHAFUNC, D3DPCMPCAPS_LESS);
+		}
+
+		*///
+
+        hr = pD3DDev->SetPixelShader(NULL);
+
+		if(FAILED(hr = pD3DDev->BeginScene()))
+			break;
+
+        hr = pD3DDev->SetFVF(D3DFVF_XYZRHW | D3DFVF_TEX1);
+		hr = pD3DDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, pVertices, sizeof(pVertices[0]));
+
+		hr = pD3DDev->EndScene();
+
+        //
+
+		pD3DDev->SetTexture(0, NULL);
+
+    	pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, abe);
+        pD3DDev->SetRenderState(D3DRS_SRCBLEND, sb);
+        pD3DDev->SetRenderState(D3DRS_DESTBLEND, db);
+
+		return S_OK;
+    }
+	while(0);
+
+    return E_FAIL;
+}
+
+//
+// CDX9SubPicAllocator
+//
+
+CDX9SubPicAllocator::CDX9SubPicAllocator(IDirect3DDevice9* pD3DDev, SIZE maxsize, bool fPow2Textures) 
+	: ISubPicAllocatorImpl(maxsize, true, fPow2Textures)
+	, m_pD3DDev(pD3DDev)
+	, m_maxsize(maxsize)
+{
+	m_pD3DDev = pD3DDev;
+	m_maxsize = maxsize;
+}
+
+// ISubPicAllocator
+
+STDMETHODIMP CDX9SubPicAllocator::ChangeDevice(IUnknown* pDev)
+{
+	CComQIPtr<IDirect3DDevice9> pD3DDev = pDev;
+	if(!pD3DDev) return E_NOINTERFACE;
+
+	CAutoLock cAutoLock(this);
+	m_pD3DDev = pD3DDev;
+
+	return __super::ChangeDevice(pDev);
+}
+
+// ISubPicAllocatorImpl
+
+bool CDX9SubPicAllocator::Alloc(bool fStatic, ISubPic** ppSubPic)
+{
+	if(!ppSubPic) 
+		return(false);
+
+	CAutoLock cAutoLock(this);
+
+	*ppSubPic = NULL;
+
+	CComPtr<IDirect3DSurface9> pSurface;
+
+	int Width = m_maxsize.cx;
+	int Height = m_maxsize.cy;
+
+	if(m_fPow2Textures)
+	{
+		Width = Height = 1;
+		while(Width < m_maxsize.cx) Width <<= 1;
+		while(Height < m_maxsize.cy) Height <<= 1;
+	}
+
+	CComPtr<IDirect3DTexture9> pTexture;
+	if(FAILED(m_pD3DDev->CreateTexture(Width, Height, 1, 0, D3DFMT_A8R8G8B8, fStatic?D3DPOOL_SYSTEMMEM:D3DPOOL_DEFAULT, &pTexture, NULL)))
+		return(false);
+
+	if(FAILED(pTexture->GetSurfaceLevel(0, &pSurface)))
+		return(false);
+
+	if(!(*ppSubPic = new CDX9SubPic(pSurface)))
+		return(false);
+
+	(*ppSubPic)->AddRef();
+
+	return(true);
+}
diff --git a/vsfilter/subpic/DX9SubPic.h b/vsfilter/subpic/DX9SubPic.h
new file mode 100644
index 0000000000000000000000000000000000000000..63493c11f16341ef419cb654d5dba5439ae3ac4b
--- /dev/null
+++ b/vsfilter/subpic/DX9SubPic.h
@@ -0,0 +1,89 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "ISubPic.h"
+
+// CDX9SubPic
+
+class CDX9SubPic : public ISubPicImpl
+{
+	CComPtr<IDirect3DSurface9> m_pSurface;
+
+protected:
+	STDMETHODIMP_(void*) GetObject(); // returns IDirect3DTexture9*
+
+public:
+	CDX9SubPic(IDirect3DSurface9* pSurface);
+
+	// ISubPic
+	STDMETHODIMP GetDesc(SubPicDesc& spd);
+	STDMETHODIMP CopyTo(ISubPic* pSubPic);
+	STDMETHODIMP ClearDirtyRect(DWORD color);
+	STDMETHODIMP Lock(SubPicDesc& spd);
+	STDMETHODIMP Unlock(RECT* pDirtyRect);
+	STDMETHODIMP AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget);
+};
+
+// CDX9SubPicAllocator
+
+class CDX9SubPicAllocator : public ISubPicAllocatorImpl, public CCritSec
+{
+	CComPtr<IDirect3DDevice9> m_pD3DDev;
+	CSize m_maxsize;
+
+	bool Alloc(bool fStatic, ISubPic** ppSubPic);
+
+public:
+	CDX9SubPicAllocator(IDirect3DDevice9* pD3DDev, SIZE maxsize, bool fPow2Textures);
+
+	// ISubPicAllocator
+	STDMETHODIMP ChangeDevice(IUnknown* pDev);
+};
+
+[uuid("CD6D2AA5-20D3-4ebe-A8A9-34D3B00CC253")]
+interface ISubRenderCallback : public IUnknown
+{
+	// NULL means release current device, textures and other resources
+	STDMETHOD(SetDevice)(IDirect3DDevice9 *dev) = 0;
+
+	// destination video rectangle, will be inside (0, 0)-(width, height)
+	// width,height is the size of the entire output window
+	STDMETHOD(Render)(REFERENCE_TIME rtStart,
+		int left, int top, int bottom, int right, 
+		int width, int height) = 0;
+};
+
+[uuid("E602585E-C05A-4828-AC69-AF92997F2E0C")]
+interface ISubRenderCallback2 : public ISubRenderCallback
+{
+	STDMETHOD(RenderEx)(REFERENCE_TIME rtStart, REFERENCE_TIME rtStop,
+		REFERENCE_TIME AvgTimePerFrame,
+		int left, int top, int right, int bottom,
+		int width, int height) = 0;
+};
+
+[uuid("9CC7F9F7-3ED1-493c-AF65-527EA1D9947F")]
+interface ISubRender : public IUnknown
+{
+	STDMETHOD(SetCallback)(ISubRenderCallback *cb) = 0;
+};
diff --git a/vsfilter/subpic/ISubPic.cpp b/vsfilter/subpic/ISubPic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f9912c59e61dc4e8882884e4a4507285fa2d7a26
--- /dev/null
+++ b/vsfilter/subpic/ISubPic.cpp
@@ -0,0 +1,859 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "ISubPic.h"
+#include "..\DSUtil\DSUtil.h"
+
+//
+// ISubPicImpl
+//
+
+ISubPicImpl::ISubPicImpl() 
+	: CUnknown(NAME("ISubPicImpl"), NULL)
+	, m_rtStart(0), m_rtStop(0)
+	, m_rcDirty(0, 0, 0, 0), m_maxsize(0, 0), m_size(0, 0), m_vidrect(0, 0, 0, 0)
+{
+}
+
+STDMETHODIMP ISubPicImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return 
+		QI(ISubPic)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPic
+
+STDMETHODIMP_(REFERENCE_TIME) ISubPicImpl::GetStart()
+{
+	return(m_rtStart);
+}
+
+STDMETHODIMP_(REFERENCE_TIME) ISubPicImpl::GetStop()
+{
+	return(m_rtStop);
+}
+
+STDMETHODIMP_(void) ISubPicImpl::SetStart(REFERENCE_TIME rtStart)
+{
+	m_rtStart = rtStart;
+}
+
+STDMETHODIMP_(void) ISubPicImpl::SetStop(REFERENCE_TIME rtStop)
+{
+	m_rtStop = rtStop;
+}
+
+STDMETHODIMP ISubPicImpl::CopyTo(ISubPic* pSubPic)
+{
+	if(!pSubPic)
+		return E_POINTER;
+
+	pSubPic->SetStart(m_rtStart);
+	pSubPic->SetStop(m_rtStop);
+	pSubPic->SetDirtyRect(m_rcDirty);
+	pSubPic->SetSize(m_size, m_vidrect);
+
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicImpl::GetDirtyRect(RECT* pDirtyRect)
+{
+	return pDirtyRect ? *pDirtyRect = m_rcDirty, S_OK : E_POINTER;
+}
+
+STDMETHODIMP ISubPicImpl::SetDirtyRect(RECT* pDirtyRect)
+{
+	return pDirtyRect ? m_rcDirty = *pDirtyRect, S_OK : E_POINTER;
+}
+
+STDMETHODIMP ISubPicImpl::GetMaxSize(SIZE* pMaxSize)
+{
+	return pMaxSize ? *pMaxSize = m_maxsize, S_OK : E_POINTER;
+}
+
+STDMETHODIMP ISubPicImpl::SetSize(SIZE size, RECT vidrect)
+{
+	m_size = size;
+	m_vidrect = vidrect;
+
+	if(m_size.cx > m_maxsize.cx)
+	{
+		m_size.cy = MulDiv(m_size.cy, m_maxsize.cx, m_size.cx);
+		m_size.cx = m_maxsize.cx;
+	}
+
+	if(m_size.cy > m_maxsize.cy)
+	{
+		m_size.cx = MulDiv(m_size.cx, m_maxsize.cy, m_size.cy);
+		m_size.cy = m_maxsize.cy;
+	}
+
+	if(m_size.cx != size.cx || m_size.cy != size.cy)
+	{
+		m_vidrect.top = MulDiv(m_vidrect.top, m_size.cx, size.cx);
+		m_vidrect.bottom = MulDiv(m_vidrect.bottom, m_size.cx, size.cx);
+		m_vidrect.left = MulDiv(m_vidrect.left, m_size.cy, size.cy);
+		m_vidrect.right = MulDiv(m_vidrect.right, m_size.cy, size.cy);
+	}
+
+	return S_OK;
+}
+
+//
+// ISubPicAllocatorImpl
+//
+
+ISubPicAllocatorImpl::ISubPicAllocatorImpl(SIZE cursize, bool fDynamicWriteOnly, bool fPow2Textures)
+	: CUnknown(NAME("ISubPicAllocatorImpl"), NULL)
+	, m_cursize(cursize)
+	, m_fDynamicWriteOnly(fDynamicWriteOnly)
+	, m_fPow2Textures(fPow2Textures)
+{
+	m_curvidrect = CRect(CPoint(0,0), m_cursize);
+}
+
+STDMETHODIMP ISubPicAllocatorImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return 
+		QI(ISubPicAllocator)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicAllocator
+
+STDMETHODIMP ISubPicAllocatorImpl::SetCurSize(SIZE cursize)
+{
+	m_cursize = cursize; 
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicAllocatorImpl::SetCurVidRect(RECT curvidrect)
+{
+	m_curvidrect = curvidrect; 
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicAllocatorImpl::GetStatic(ISubPic** ppSubPic)
+{
+	if(!ppSubPic)
+		return E_POINTER;
+
+	if(!m_pStatic)
+	{
+		if(!Alloc(true, &m_pStatic) || !m_pStatic) 
+			return E_OUTOFMEMORY;
+	}
+
+	m_pStatic->SetSize(m_cursize, m_curvidrect);
+
+	(*ppSubPic = m_pStatic)->AddRef();
+
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicAllocatorImpl::AllocDynamic(ISubPic** ppSubPic)
+{
+	if(!ppSubPic)
+		return E_POINTER;
+
+	if(!Alloc(false, ppSubPic) || !*ppSubPic)
+		return E_OUTOFMEMORY;
+
+	(*ppSubPic)->SetSize(m_cursize, m_curvidrect);
+
+	return S_OK;
+}
+
+STDMETHODIMP_(bool) ISubPicAllocatorImpl::IsDynamicWriteOnly()
+{
+	return(m_fDynamicWriteOnly);
+}
+
+STDMETHODIMP ISubPicAllocatorImpl::ChangeDevice(IUnknown* pDev)
+{
+	m_pStatic = NULL;
+	return S_OK;
+}
+
+//
+// ISubPicProviderImpl
+//
+
+ISubPicProviderImpl::ISubPicProviderImpl(CCritSec* pLock)
+	: CUnknown(NAME("ISubPicProviderImpl"), NULL)
+	, m_pLock(pLock)
+{
+}
+
+ISubPicProviderImpl::~ISubPicProviderImpl()
+{
+}
+
+STDMETHODIMP ISubPicProviderImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return
+		QI(ISubPicProvider)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicProvider
+
+STDMETHODIMP ISubPicProviderImpl::Lock()
+{
+	return m_pLock ? m_pLock->Lock(), S_OK : E_FAIL;
+}
+
+STDMETHODIMP ISubPicProviderImpl::Unlock()
+{
+	return m_pLock ? m_pLock->Unlock(), S_OK : E_FAIL;
+}
+
+//
+// ISubPicQueueImpl
+//
+
+ISubPicQueueImpl::ISubPicQueueImpl(ISubPicAllocator* pAllocator, HRESULT* phr) 
+	: CUnknown(NAME("ISubPicQueueImpl"), NULL)
+	, m_pAllocator(pAllocator)
+	, m_rtNow(0)
+	, m_fps(25.0)
+{
+	if(phr) *phr = S_OK;
+
+	if(!m_pAllocator)
+	{
+		if(phr) *phr = E_FAIL;
+		return;
+	}
+}
+
+ISubPicQueueImpl::~ISubPicQueueImpl()
+{
+}
+
+STDMETHODIMP ISubPicQueueImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return 
+		QI(ISubPicQueue)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicQueue
+
+STDMETHODIMP ISubPicQueueImpl::SetSubPicProvider(ISubPicProvider* pSubPicProvider)
+{
+	CAutoLock cAutoLock(&m_csSubPicProvider);
+
+//	if(m_pSubPicProvider != pSubPicProvider)
+	{
+		m_pSubPicProvider = pSubPicProvider;
+
+		Invalidate();
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicQueueImpl::GetSubPicProvider(ISubPicProvider** pSubPicProvider)
+{
+	if(!pSubPicProvider)
+		return E_POINTER;
+
+	CAutoLock cAutoLock(&m_csSubPicProvider);
+
+	if(m_pSubPicProvider)
+		(*pSubPicProvider = m_pSubPicProvider)->AddRef();
+
+	return !!*pSubPicProvider ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP ISubPicQueueImpl::SetFPS(double fps)
+{
+	m_fps = fps;
+
+	return S_OK;
+}
+
+STDMETHODIMP ISubPicQueueImpl::SetTime(REFERENCE_TIME rtNow)
+{
+	m_rtNow = rtNow;
+
+	return S_OK;
+}
+
+// private
+
+HRESULT ISubPicQueueImpl::RenderTo(ISubPic* pSubPic, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double fps)
+{
+	HRESULT hr = E_FAIL;
+
+	if(!pSubPic)
+		return hr;
+
+	CComPtr<ISubPicProvider> pSubPicProvider;
+	if(FAILED(GetSubPicProvider(&pSubPicProvider)) || !pSubPicProvider)
+		return hr;
+
+	if(FAILED(pSubPicProvider->Lock()))
+		return hr;
+
+	SubPicDesc spd;
+	if(SUCCEEDED(pSubPic->ClearDirtyRect(0xFF000000))
+	&& SUCCEEDED(pSubPic->Lock(spd)))
+	{
+		CRect r(0,0,0,0);
+		hr = pSubPicProvider->Render(spd, (rtStart+rtStop)/2, fps, r);
+
+		pSubPic->SetStart(rtStart);
+		pSubPic->SetStop(rtStop);
+
+		pSubPic->Unlock(r);
+	}
+
+	pSubPicProvider->Unlock();
+
+	return hr;
+}
+
+//
+// CSubPicQueue
+//
+
+CSubPicQueue::CSubPicQueue(int nMaxSubPic, ISubPicAllocator* pAllocator, HRESULT* phr) 
+	: ISubPicQueueImpl(pAllocator, phr)
+	, m_nMaxSubPic(nMaxSubPic)
+	, m_rtQueueStart(0)
+{
+	if(phr && FAILED(*phr))
+		return;
+
+	if(m_nMaxSubPic < 1)
+		{if(phr) *phr = E_INVALIDARG; return;}
+
+	m_fBreakBuffering = false;
+	for(int i = 0; i < EVENT_COUNT; i++) 
+		m_ThreadEvents[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
+	CAMThread::Create();
+}
+
+CSubPicQueue::~CSubPicQueue()
+{
+	m_fBreakBuffering = true;
+	SetEvent(m_ThreadEvents[EVENT_EXIT]);
+	CAMThread::Close();
+	for(int i = 0; i < EVENT_COUNT; i++) 
+		CloseHandle(m_ThreadEvents[i]);
+}
+
+// ISubPicQueue
+
+STDMETHODIMP CSubPicQueue::SetFPS(double fps)
+{
+	HRESULT hr = __super::SetFPS(fps);
+	if(FAILED(hr)) return hr;
+
+	SetEvent(m_ThreadEvents[EVENT_TIME]);
+
+	return S_OK;
+}
+
+STDMETHODIMP CSubPicQueue::SetTime(REFERENCE_TIME rtNow)
+{
+	HRESULT hr = __super::SetTime(rtNow);
+	if(FAILED(hr)) return hr;
+
+	SetEvent(m_ThreadEvents[EVENT_TIME]);
+
+	return S_OK;
+}
+
+STDMETHODIMP CSubPicQueue::Invalidate(REFERENCE_TIME rtInvalidate)
+{
+	{
+//		CAutoLock cQueueLock(&m_csQueueLock);
+//		RemoveAll();
+
+		m_rtInvalidate = rtInvalidate;
+		m_fBreakBuffering = true;
+		SetEvent(m_ThreadEvents[EVENT_TIME]);
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP_(bool) CSubPicQueue::LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic)
+{
+	if(!ppSubPic)
+		return(false);
+
+	*ppSubPic = NULL;
+
+	CAutoLock cQueueLock(&m_csQueueLock);
+
+	POSITION pos = GetHeadPosition();
+	while(pos)
+	{
+		CComPtr<ISubPic> pSubPic = GetNext(pos);
+		if(pSubPic->GetStart() <= rtNow && rtNow < pSubPic->GetStop())
+		{
+			*ppSubPic = pSubPic.Detach();
+			break;
+		}
+	}
+
+	return(!!*ppSubPic);
+}
+
+STDMETHODIMP CSubPicQueue::GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop)
+{
+	CAutoLock cQueueLock(&m_csQueueLock);
+
+	nSubPics = GetCount();
+	rtNow = m_rtNow;
+	rtStart = m_rtQueueStart;
+	rtStop = GetCount() > 0 ? GetTail()->GetStop() : rtStart;
+
+	return S_OK;
+}
+
+STDMETHODIMP CSubPicQueue::GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop)
+{
+	CAutoLock cQueueLock(&m_csQueueLock);
+
+	rtStart = rtStop = -1;
+
+	if(nSubPic >= 0 && nSubPic < (int)GetCount())
+	{
+		if(POSITION pos = FindIndex(nSubPic))
+		{
+			rtStart = GetAt(pos)->GetStart();
+			rtStop = GetAt(pos)->GetStop();
+		}
+	}
+	else
+	{
+		return E_INVALIDARG;
+	}
+
+	return S_OK;
+}
+
+// private
+
+REFERENCE_TIME CSubPicQueue::UpdateQueue()
+{
+	CAutoLock cQueueLock(&m_csQueueLock);
+
+	REFERENCE_TIME rtNow = m_rtNow;
+
+	if(rtNow < m_rtQueueStart)
+	{
+		RemoveAll();
+	}
+	else
+	{
+		while(GetCount() > 0 && rtNow >= GetHead()->GetStop())
+			RemoveHead();
+	}
+
+	m_rtQueueStart = rtNow;
+
+	if(GetCount() > 0)
+		rtNow = GetTail()->GetStop();
+
+	return(rtNow);
+}
+
+void CSubPicQueue::AppendQueue(ISubPic* pSubPic)
+{
+	CAutoLock cQueueLock(&m_csQueueLock);
+
+	AddTail(pSubPic);
+}
+
+// overrides
+
+DWORD CSubPicQueue::ThreadProc()
+{	
+	SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST/*THREAD_PRIORITY_BELOW_NORMAL*/);
+
+	while((WaitForMultipleObjects(EVENT_COUNT, m_ThreadEvents, FALSE, INFINITE) - WAIT_OBJECT_0) == EVENT_TIME)
+	{
+		double fps = m_fps;
+		REFERENCE_TIME rtNow = UpdateQueue();
+
+		int nMaxSubPic = m_nMaxSubPic;
+
+		CComPtr<ISubPicProvider> pSubPicProvider;
+		if(SUCCEEDED(GetSubPicProvider(&pSubPicProvider)) && pSubPicProvider
+		&& SUCCEEDED(pSubPicProvider->Lock()))
+		{
+			for(POSITION pos = pSubPicProvider->GetStartPosition(rtNow, fps); 
+				pos && !m_fBreakBuffering && GetCount() < (size_t)nMaxSubPic; 
+				pos = pSubPicProvider->GetNext(pos))
+			{
+				REFERENCE_TIME rtStart = pSubPicProvider->GetStart(pos, fps);
+				REFERENCE_TIME rtStop = pSubPicProvider->GetStop(pos, fps);
+
+				if(m_rtNow >= rtStart)
+				{
+//						m_fBufferUnderrun = true;
+					if(m_rtNow >= rtStop) continue;
+				}
+
+				if(rtStart >= m_rtNow + 60*10000000i64) // we are already one minute ahead, this should be enough
+					break;
+
+				if(rtNow < rtStop)
+				{
+					CComPtr<ISubPic> pStatic;
+					if(FAILED(m_pAllocator->GetStatic(&pStatic)))
+						break;
+
+					HRESULT hr = RenderTo(pStatic, rtStart, rtStop, fps);
+
+					if(FAILED(hr))
+						break;
+
+					if(S_OK != hr) // subpic was probably empty
+						continue;
+
+					CComPtr<ISubPic> pDynamic;
+					if(FAILED(m_pAllocator->AllocDynamic(&pDynamic))
+					|| FAILED(pStatic->CopyTo(pDynamic)))
+						break;
+
+					AppendQueue(pDynamic);
+				}
+			}
+
+			pSubPicProvider->Unlock();
+		}
+
+		if(m_fBreakBuffering)
+		{
+			CAutoLock cQueueLock(&m_csQueueLock);
+
+			REFERENCE_TIME rtInvalidate = m_rtInvalidate;
+
+			while(GetCount() && GetTail()->GetStop() > rtInvalidate)
+			{
+				if(GetTail()->GetStart() < rtInvalidate) GetTail()->SetStop(rtInvalidate);
+				else RemoveTail();
+			}
+
+			m_fBreakBuffering = false;
+		}
+	}
+
+	return(0);
+}
+
+//
+// CSubPicQueueNoThread
+//
+
+CSubPicQueueNoThread::CSubPicQueueNoThread(ISubPicAllocator* pAllocator, HRESULT* phr) 
+	: ISubPicQueueImpl(pAllocator, phr)
+{
+}
+
+CSubPicQueueNoThread::~CSubPicQueueNoThread()
+{
+}
+
+// ISubPicQueue
+
+STDMETHODIMP CSubPicQueueNoThread::Invalidate(REFERENCE_TIME rtInvalidate)
+{
+	CAutoLock cQueueLock(&m_csLock);
+
+	m_pSubPic = NULL;
+
+	return S_OK;
+}
+
+STDMETHODIMP_(bool) CSubPicQueueNoThread::LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic)
+{
+	if(!ppSubPic)
+		return(false);
+
+	*ppSubPic = NULL;
+
+	CComPtr<ISubPic> pSubPic;
+
+	{
+		CAutoLock cAutoLock(&m_csLock);
+
+		if(!m_pSubPic)
+		{
+			if(FAILED(m_pAllocator->AllocDynamic(&m_pSubPic)))
+				return(false);
+		}
+
+		pSubPic = m_pSubPic;
+	}
+
+	if(pSubPic->GetStart() <= rtNow && rtNow < pSubPic->GetStop())
+	{
+		(*ppSubPic = pSubPic)->AddRef();
+	}
+	else
+	{
+		CComPtr<ISubPicProvider> pSubPicProvider;
+		if(SUCCEEDED(GetSubPicProvider(&pSubPicProvider)) && pSubPicProvider
+		&& SUCCEEDED(pSubPicProvider->Lock()))
+		{
+			double fps = m_fps;
+
+			if(POSITION pos = pSubPicProvider->GetStartPosition(rtNow, fps))
+			{
+				REFERENCE_TIME rtStart = pSubPicProvider->GetStart(pos, fps);
+				REFERENCE_TIME rtStop = pSubPicProvider->GetStop(pos, fps);
+
+				if(pSubPicProvider->IsAnimated(pos))
+				{
+					rtStart = rtNow;
+					rtStop = rtNow+1;
+				}
+
+				if(rtStart <= rtNow && rtNow < rtStop)
+				{
+					if(m_pAllocator->IsDynamicWriteOnly())
+					{
+						CComPtr<ISubPic> pStatic;
+						if(SUCCEEDED(m_pAllocator->GetStatic(&pStatic))
+						&& SUCCEEDED(RenderTo(pStatic, rtStart, rtStop, fps))
+						&& SUCCEEDED(pStatic->CopyTo(pSubPic)))
+							(*ppSubPic = pSubPic)->AddRef();
+					}
+					else
+					{
+						if(SUCCEEDED(RenderTo(m_pSubPic, rtStart, rtStop, fps)))
+							(*ppSubPic = pSubPic)->AddRef();
+					}
+				}
+			}
+
+			pSubPicProvider->Unlock();
+
+			if(*ppSubPic)
+			{
+				CAutoLock cAutoLock(&m_csLock);
+
+				m_pSubPic = *ppSubPic;
+			}
+		}
+	}
+
+	return(!!*ppSubPic);
+}
+
+STDMETHODIMP CSubPicQueueNoThread::GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop)
+{
+	CAutoLock cAutoLock(&m_csLock);
+
+	nSubPics = 0;
+	rtNow = m_rtNow;
+	rtStart = rtStop = 0;
+
+	if(m_pSubPic)
+	{
+		nSubPics = 1;
+		rtStart = m_pSubPic->GetStart();
+		rtStop = m_pSubPic->GetStop();
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP CSubPicQueueNoThread::GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop)
+{
+	CAutoLock cAutoLock(&m_csLock);
+
+	if(!m_pSubPic || nSubPic != 0)
+		return E_INVALIDARG;
+
+	rtStart = m_pSubPic->GetStart();
+	rtStop = m_pSubPic->GetStop();
+
+	return S_OK;
+}
+
+//
+// ISubPicAllocatorPresenterImpl
+//
+
+ISubPicAllocatorPresenterImpl::ISubPicAllocatorPresenterImpl(HWND hWnd, HRESULT& hr)
+	: CUnknown(NAME("ISubPicAllocatorPresenterImpl"), NULL)
+	, m_hWnd(hWnd)
+	, m_NativeVideoSize(0, 0), m_AspectRatio(0, 0)
+	, m_VideoRect(0, 0, 0, 0), m_WindowRect(0, 0, 0, 0)
+	, m_fps(25.0)
+{
+    if(!IsWindow(m_hWnd)) {hr = E_INVALIDARG; return;}
+	GetWindowRect(m_hWnd, &m_WindowRect);
+	SetVideoAngle(Vector(), false);
+	hr = S_OK;
+}
+
+ISubPicAllocatorPresenterImpl::~ISubPicAllocatorPresenterImpl()
+{
+}
+
+STDMETHODIMP ISubPicAllocatorPresenterImpl::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return 
+		QI(ISubPicAllocatorPresenter)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+void ISubPicAllocatorPresenterImpl::AlphaBltSubPic(CSize size, SubPicDesc* pTarget)
+{
+	CComPtr<ISubPic> pSubPic;
+	if(m_pSubPicQueue->LookupSubPic(m_rtNow, &pSubPic))
+	{
+		SubPicDesc spd;
+		pSubPic->GetDesc(spd);
+
+		if(spd.w > 0 && spd.h > 0)
+		{
+			CRect r;
+			pSubPic->GetDirtyRect(r);
+
+			// FIXME
+			r.DeflateRect(1, 1);
+
+			CRect rDstText(
+				r.left * size.cx / spd.w,
+				r.top * size.cy / spd.h,
+				r.right * size.cx / spd.w,
+				r.bottom * size.cy / spd.h);
+
+			pSubPic->AlphaBlt(r, rDstText, pTarget);
+		}
+	}
+}
+
+// ISubPicAllocatorPresenter
+
+STDMETHODIMP_(SIZE) ISubPicAllocatorPresenterImpl::GetVideoSize(bool fCorrectAR)
+{
+	CSize VideoSize(m_NativeVideoSize);
+
+	if(fCorrectAR && m_AspectRatio.cx > 0 && m_AspectRatio.cy > 0)
+		VideoSize.cx = VideoSize.cy*m_AspectRatio.cx/m_AspectRatio.cy;
+
+	return(VideoSize);
+}
+
+STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetPosition(RECT w, RECT v)
+{
+	bool fWindowPosChanged = !!(m_WindowRect != w);
+	bool fWindowSizeChanged = !!(m_WindowRect.Size() != CRect(w).Size());
+
+	m_WindowRect = w;
+
+	bool fVideoRectChanged = !!(m_VideoRect != v);
+
+	m_VideoRect = v;
+
+	if(fWindowSizeChanged || fVideoRectChanged)
+	{
+		if(m_pAllocator)
+		{
+			m_pAllocator->SetCurSize(m_WindowRect.Size());
+			m_pAllocator->SetCurVidRect(m_VideoRect);
+		}
+
+		if(m_pSubPicQueue)
+		{
+			m_pSubPicQueue->Invalidate();
+		}
+	}
+
+	if(fWindowPosChanged || fVideoRectChanged)
+		Paint(fWindowSizeChanged || fVideoRectChanged);
+}
+
+STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetTime(REFERENCE_TIME rtNow)
+{
+/*
+	if(m_rtNow <= rtNow && rtNow <= m_rtNow + 1000000)
+		return;
+*/
+	m_rtNow = rtNow;
+
+	if(m_pSubPicQueue)
+	{
+		m_pSubPicQueue->SetTime(rtNow);
+	}
+}
+
+STDMETHODIMP_(double) ISubPicAllocatorPresenterImpl::GetFPS()
+{
+	return(m_fps);
+}
+
+STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::SetSubPicProvider(ISubPicProvider* pSubPicProvider)
+{
+	m_SubPicProvider = pSubPicProvider;
+
+	if(m_pSubPicQueue)
+		m_pSubPicQueue->SetSubPicProvider(pSubPicProvider);
+}
+
+STDMETHODIMP_(void) ISubPicAllocatorPresenterImpl::Invalidate(REFERENCE_TIME rtInvalidate)
+{
+	if(m_pSubPicQueue)
+		m_pSubPicQueue->Invalidate(rtInvalidate);
+}
+
+#include <math.h>
+
+void ISubPicAllocatorPresenterImpl::Transform(CRect r, Vector v[4])
+{
+	v[0] = Vector(r.left, r.top, 0);
+	v[1] = Vector(r.right, r.top, 0);
+	v[2] = Vector(r.left, r.bottom, 0);
+	v[3] = Vector(r.right, r.bottom, 0);
+
+	Vector center(r.CenterPoint().x, r.CenterPoint().y, 0);
+	int l = (int)(Vector(r.Size().cx, r.Size().cy, 0).Length()*1.5f)+1;
+
+	for(int i = 0; i < 4; i++)
+	{
+		v[i] = m_xform << (v[i] - center);
+		v[i].z = v[i].z / l + 0.5f;
+		v[i].x /= v[i].z*2;
+		v[i].y /= v[i].z*2;
+		v[i] += center;
+	}
+}
+
+STDMETHODIMP ISubPicAllocatorPresenterImpl::SetVideoAngle(Vector v, bool fRepaint)
+{
+	m_xform = XForm(Ray(Vector(0, 0, 0), v), Vector(1, 1, 1), false);
+	if(fRepaint) Paint(true);
+	return S_OK;
+}
diff --git a/vsfilter/subpic/ISubPic.h b/vsfilter/subpic/ISubPic.h
new file mode 100644
index 0000000000000000000000000000000000000000..06a9cbaa0f14653339374b80941cb174a5864f6b
--- /dev/null
+++ b/vsfilter/subpic/ISubPic.h
@@ -0,0 +1,405 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlbase.h>
+#include <atlcoll.h>
+#include "CoordGeom.h"
+
+#pragma pack(push, 1)
+struct SubPicDesc
+{
+	int type;
+	int w, h, bpp, pitch, pitchUV;
+	void* bits;
+	BYTE* bitsU;
+	BYTE* bitsV;
+	RECT vidrect; // video rectangle
+
+	struct SubPicDesc() {type = 0; w = h = bpp = pitch = pitchUV = 0; bits = NULL; bitsU = bitsV = NULL;}
+};
+#pragma pack(pop)
+
+//
+// ISubPic
+//
+
+[uuid("449E11F3-52D1-4a27-AA61-E2733AC92CC0")]
+interface ISubPic : public IUnknown
+{
+	STDMETHOD_(void*, GetObject) () PURE;
+
+	STDMETHOD_(REFERENCE_TIME, GetStart) () PURE;
+	STDMETHOD_(REFERENCE_TIME, GetStop) () PURE;
+	STDMETHOD_(void, SetStart) (REFERENCE_TIME rtStart) PURE;
+	STDMETHOD_(void, SetStop) (REFERENCE_TIME rtStop) PURE;
+
+	STDMETHOD (GetDesc) (SubPicDesc& spd /*[out]*/) PURE;
+	STDMETHOD (CopyTo) (ISubPic* pSubPic /*[in]*/) PURE;
+
+	STDMETHOD (ClearDirtyRect) (DWORD color /*[in]*/) PURE;
+	STDMETHOD (GetDirtyRect) (RECT* pDirtyRect /*[out]*/) PURE;
+	STDMETHOD (SetDirtyRect) (RECT* pDirtyRect /*[in]*/) PURE;
+
+	STDMETHOD (GetMaxSize) (SIZE* pMaxSize /*[out]*/) PURE;
+	STDMETHOD (SetSize) (SIZE pSize /*[in]*/, RECT vidrect /*[in]*/) PURE;
+
+	STDMETHOD (Lock) (SubPicDesc& spd /*[out]*/) PURE;
+	STDMETHOD (Unlock) (RECT* pDirtyRect /*[in]*/) PURE;
+
+	STDMETHOD (AlphaBlt) (RECT* pSrc, RECT* pDst, SubPicDesc* pTarget = NULL /*[in]*/) PURE;
+};
+
+class ISubPicImpl : public CUnknown, public ISubPic
+{
+protected:
+	REFERENCE_TIME m_rtStart, m_rtStop;
+	CRect m_rcDirty;
+	CSize m_maxsize, m_size;
+	CRect m_vidrect;
+
+public:
+	ISubPicImpl();
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPic
+
+	STDMETHODIMP_(REFERENCE_TIME) GetStart();
+	STDMETHODIMP_(REFERENCE_TIME) GetStop();
+	STDMETHODIMP_(void) SetStart(REFERENCE_TIME rtStart);
+	STDMETHODIMP_(void) SetStop(REFERENCE_TIME rtStop);
+
+	STDMETHODIMP GetDesc(SubPicDesc& spd) = 0;
+	STDMETHODIMP CopyTo(ISubPic* pSubPic);
+
+	STDMETHODIMP ClearDirtyRect(DWORD color) = 0;
+	STDMETHODIMP GetDirtyRect(RECT* pDirtyRect);
+	STDMETHODIMP SetDirtyRect(RECT* pDirtyRect);
+
+	STDMETHODIMP GetMaxSize(SIZE* pMaxSize);
+	STDMETHODIMP SetSize(SIZE size, RECT vidrect);
+
+	STDMETHODIMP Lock(SubPicDesc& spd) = 0;
+	STDMETHODIMP Unlock(RECT* pDirtyRect) = 0;
+
+	STDMETHODIMP AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget) = 0;
+};
+
+//
+// ISubPicAllocator
+//
+
+[uuid("CF7C3C23-6392-4a42-9E72-0736CFF793CB")]
+interface ISubPicAllocator : public IUnknown
+{
+	STDMETHOD (SetCurSize) (SIZE size /*[in]*/) PURE;
+	STDMETHOD (SetCurVidRect) (RECT curvidrect) PURE;
+
+	STDMETHOD (GetStatic) (ISubPic** ppSubPic /*[out]*/) PURE;
+	STDMETHOD (AllocDynamic) (ISubPic** ppSubPic /*[out]*/) PURE;
+
+	STDMETHOD_(bool, IsDynamicWriteOnly) () PURE;
+
+	STDMETHOD (ChangeDevice) (IUnknown* pDev) PURE;
+};
+
+
+class ISubPicAllocatorImpl : public CUnknown, public ISubPicAllocator
+{
+	CComPtr<ISubPic> m_pStatic;
+
+private:
+	CSize m_cursize;
+	CRect m_curvidrect;
+	bool m_fDynamicWriteOnly;
+
+	virtual bool Alloc(bool fStatic, ISubPic** ppSubPic) = 0;
+
+protected:
+	bool m_fPow2Textures;
+
+public:
+	ISubPicAllocatorImpl(SIZE cursize, bool fDynamicWriteOnly, bool fPow2Textures);
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicAllocator
+
+	STDMETHODIMP SetCurSize(SIZE cursize);
+	STDMETHODIMP SetCurVidRect(RECT curvidrect);
+	STDMETHODIMP GetStatic(ISubPic** ppSubPic);
+	STDMETHODIMP AllocDynamic(ISubPic** ppSubPic);
+	STDMETHODIMP_(bool) IsDynamicWriteOnly();
+	STDMETHODIMP ChangeDevice(IUnknown* pDev);
+};
+
+//
+// ISubPicProvider
+//
+
+[uuid("D62B9A1A-879A-42db-AB04-88AA8F243CFD")]
+interface ISubPicProvider : public IUnknown
+{
+	STDMETHOD (Lock) () PURE;
+	STDMETHOD (Unlock) () PURE;
+
+	STDMETHOD_(POSITION, GetStartPosition) (REFERENCE_TIME rt, double fps) PURE;
+	STDMETHOD_(POSITION, GetNext) (POSITION pos) PURE;
+
+	STDMETHOD_(REFERENCE_TIME, GetStart) (POSITION pos, double fps) PURE;
+	STDMETHOD_(REFERENCE_TIME, GetStop) (POSITION pos, double fps) PURE;
+
+	STDMETHOD_(bool, IsAnimated) (POSITION pos) PURE;
+
+	STDMETHOD (Render) (SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox) PURE;
+};
+
+class ISubPicProviderImpl : public CUnknown, public ISubPicProvider
+{
+protected:
+	CCritSec* m_pLock;
+
+public:
+	ISubPicProviderImpl(CCritSec* pLock);
+	virtual ~ISubPicProviderImpl();
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicProvider
+
+	STDMETHODIMP Lock();
+	STDMETHODIMP Unlock();
+
+	STDMETHODIMP_(POSITION) GetStartPosition(REFERENCE_TIME rt, double fps) = 0;
+	STDMETHODIMP_(POSITION) GetNext(POSITION pos) = 0;
+
+	STDMETHODIMP_(REFERENCE_TIME) GetStart(POSITION pos, double fps) = 0;
+	STDMETHODIMP_(REFERENCE_TIME) GetStop(POSITION pos, double fps) = 0;
+
+	STDMETHODIMP Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox) = 0;
+};
+
+//
+// ISubPicQueue
+//
+
+[uuid("C8334466-CD1E-4ad1-9D2D-8EE8519BD180")]
+interface ISubPicQueue : public IUnknown
+{
+	STDMETHOD (SetSubPicProvider) (ISubPicProvider* pSubPicProvider /*[in]*/) PURE;
+	STDMETHOD (GetSubPicProvider) (ISubPicProvider** pSubPicProvider /*[out]*/) PURE;
+
+	STDMETHOD (SetFPS) (double fps /*[in]*/) PURE;
+	STDMETHOD (SetTime) (REFERENCE_TIME rtNow /*[in]*/) PURE;
+
+	STDMETHOD (Invalidate) (REFERENCE_TIME rtInvalidate = -1) PURE;
+	STDMETHOD_(bool, LookupSubPic) (REFERENCE_TIME rtNow /*[in]*/, ISubPic** ppSubPic /*[out]*/) PURE;
+
+	STDMETHOD (GetStats) (int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop /*[out]*/) PURE;
+	STDMETHOD (GetStats) (int nSubPic /*[in]*/, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop /*[out]*/) PURE;
+};
+
+class ISubPicQueueImpl : public CUnknown, public ISubPicQueue
+{
+	CCritSec m_csSubPicProvider;
+	CComPtr<ISubPicProvider> m_pSubPicProvider;
+
+protected:
+	double m_fps;
+	REFERENCE_TIME m_rtNow;
+
+	CComPtr<ISubPicAllocator> m_pAllocator;
+
+	HRESULT RenderTo(ISubPic* pSubPic, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, double fps);
+
+public:
+	ISubPicQueueImpl(ISubPicAllocator* pAllocator, HRESULT* phr);
+	virtual ~ISubPicQueueImpl();
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicQueue
+
+	STDMETHODIMP SetSubPicProvider(ISubPicProvider* pSubPicProvider);
+	STDMETHODIMP GetSubPicProvider(ISubPicProvider** pSubPicProvider);
+
+	STDMETHODIMP SetFPS(double fps);
+	STDMETHODIMP SetTime(REFERENCE_TIME rtNow);
+/*
+	STDMETHODIMP Invalidate(REFERENCE_TIME rtInvalidate = -1) = 0;
+	STDMETHODIMP_(bool) LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic) = 0;
+
+	STDMETHODIMP GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) = 0;
+	STDMETHODIMP GetStats(int nSubPics, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop) = 0;
+*/
+};
+
+class CSubPicQueue : public ISubPicQueueImpl, private CInterfaceList<ISubPic>, private CAMThread
+{
+	int m_nMaxSubPic;
+
+	CCritSec m_csQueueLock; // for protecting CInterfaceList<ISubPic>
+	REFERENCE_TIME UpdateQueue();
+	void AppendQueue(ISubPic* pSubPic);
+
+	REFERENCE_TIME m_rtQueueStart, m_rtInvalidate;
+
+	// CAMThread
+
+	bool m_fBreakBuffering;
+	enum {EVENT_EXIT, EVENT_TIME, EVENT_COUNT}; // IMPORTANT: _EXIT must come before _TIME if we want to exit fast from the destructor
+	HANDLE m_ThreadEvents[EVENT_COUNT];
+    DWORD ThreadProc();
+
+public:
+	CSubPicQueue(int nMaxSubPic, ISubPicAllocator* pAllocator, HRESULT* phr);
+	virtual ~CSubPicQueue();
+
+	// ISubPicQueue
+
+	STDMETHODIMP SetFPS(double fps);
+	STDMETHODIMP SetTime(REFERENCE_TIME rtNow);
+
+	STDMETHODIMP Invalidate(REFERENCE_TIME rtInvalidate = -1);
+	STDMETHODIMP_(bool) LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic);
+
+	STDMETHODIMP GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop);
+	STDMETHODIMP GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop);
+};
+
+class CSubPicQueueNoThread : public ISubPicQueueImpl
+{
+	CCritSec m_csLock;
+	CComPtr<ISubPic> m_pSubPic;
+
+public:
+	CSubPicQueueNoThread(ISubPicAllocator* pAllocator, HRESULT* phr);
+	virtual ~CSubPicQueueNoThread();
+
+	// ISubPicQueue
+
+	STDMETHODIMP Invalidate(REFERENCE_TIME rtInvalidate = -1);
+	STDMETHODIMP_(bool) LookupSubPic(REFERENCE_TIME rtNow, ISubPic** ppSubPic);
+
+	STDMETHODIMP GetStats(int& nSubPics, REFERENCE_TIME& rtNow, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop);
+	STDMETHODIMP GetStats(int nSubPic, REFERENCE_TIME& rtStart, REFERENCE_TIME& rtStop);
+};
+
+//
+// ISubPicAllocatorPresenter
+//
+
+[uuid("CF75B1F0-535C-4074-8869-B15F177F944E")]
+interface ISubPicAllocatorPresenter : public IUnknown
+{
+	STDMETHOD (CreateRenderer) (IUnknown** ppRenderer) PURE;
+
+	STDMETHOD_(SIZE, GetVideoSize) (bool fCorrectAR = true) PURE;
+	STDMETHOD_(void, SetPosition) (RECT w, RECT v) PURE;
+	STDMETHOD_(bool, Paint) (bool fAll) PURE;
+
+	STDMETHOD_(void, SetTime) (REFERENCE_TIME rtNow) PURE;
+	STDMETHOD_(double, GetFPS) () PURE;
+
+	STDMETHOD_(void, SetSubPicProvider) (ISubPicProvider* pSubPicProvider) PURE;
+	STDMETHOD_(void, Invalidate) (REFERENCE_TIME rtInvalidate = -1) PURE;
+
+	STDMETHOD (GetDIB) (BYTE* lpDib, DWORD* size) PURE;
+
+	STDMETHOD (SetVideoAngle) (Vector v, bool fRepaint = true) PURE;
+	STDMETHOD (SetPixelShader) (LPCSTR pSrcData, LPCSTR pTarget) PURE;
+};
+
+class ISubPicAllocatorPresenterImpl 
+	: public CUnknown
+	, public CCritSec
+	, public ISubPicAllocatorPresenter
+{
+protected:
+	HWND m_hWnd;
+	CSize m_spMaxSize; // TODO:
+	int m_spMaxQueued; // TODO:
+
+	CSize m_NativeVideoSize, m_AspectRatio;
+	CRect m_VideoRect, m_WindowRect;
+
+	REFERENCE_TIME m_rtNow;
+	double m_fps;
+
+	CComPtr<ISubPicProvider> m_SubPicProvider;
+	CComPtr<ISubPicAllocator> m_pAllocator;
+	CComPtr<ISubPicQueue> m_pSubPicQueue;
+
+	void AlphaBltSubPic(CSize size, SubPicDesc* pTarget = NULL);
+
+    XForm m_xform;
+	void Transform(CRect r, Vector v[4]);
+
+public:
+	ISubPicAllocatorPresenterImpl(HWND hWnd, HRESULT& hr);
+	virtual ~ISubPicAllocatorPresenterImpl();
+
+	DECLARE_IUNKNOWN;
+	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicAllocatorPresenter
+
+	STDMETHODIMP CreateRenderer(IUnknown** ppRenderer) = 0;
+
+	STDMETHODIMP_(SIZE) GetVideoSize(bool fCorrectAR = true);
+	STDMETHODIMP_(void) SetPosition(RECT w, RECT v);
+	STDMETHODIMP_(bool) Paint(bool fAll) = 0;
+
+	STDMETHODIMP_(void) SetTime(REFERENCE_TIME rtNow);
+	STDMETHODIMP_(double) GetFPS();
+
+	STDMETHODIMP_(void) SetSubPicProvider(ISubPicProvider* pSubPicProvider);	
+	STDMETHODIMP_(void) Invalidate(REFERENCE_TIME rtInvalidate = -1);
+
+	STDMETHODIMP GetDIB(BYTE* lpDib, DWORD* size) {return E_NOTIMPL;}
+
+	STDMETHODIMP SetVideoAngle(Vector v, bool fRepaint = true);
+	STDMETHODIMP SetPixelShader(LPCSTR pSrcData, LPCSTR pTarget) {return E_NOTIMPL;}
+};
+
+//
+// ISubStream
+//
+
+[uuid("DE11E2FB-02D3-45e4-A174-6B7CE2783BDB")]
+interface ISubStream : public IPersist
+{
+	STDMETHOD_(int, GetStreamCount) () PURE;
+	STDMETHOD (GetStreamInfo) (int i, WCHAR** ppName, LCID* pLCID) PURE;
+	STDMETHOD_(int, GetStream) () PURE;
+	STDMETHOD (SetStream) (int iStream) PURE;
+	STDMETHOD (Reload) () PURE;
+
+	// TODO: get rid of IPersist to identify type and use only 
+	// interface functions to modify the settings of the substream
+};
+
diff --git a/vsfilter/subpic/MemSubPic.cpp b/vsfilter/subpic/MemSubPic.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e266d6770e85f18448d4108f8dcc0382ae49e397
--- /dev/null
+++ b/vsfilter/subpic/MemSubPic.cpp
@@ -0,0 +1,575 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "MemSubPic.h"
+
+// color conv
+
+unsigned char Clip_base[256*3];
+unsigned char* Clip = Clip_base + 256;
+
+const int c2y_cyb = int(0.114*219/255*65536+0.5);
+const int c2y_cyg = int(0.587*219/255*65536+0.5);
+const int c2y_cyr = int(0.299*219/255*65536+0.5);
+const int c2y_cu = int(1.0/2.018*1024+0.5);
+const int c2y_cv = int(1.0/1.596*1024+0.5);
+
+int c2y_yb[256];
+int c2y_yg[256];
+int c2y_yr[256];
+
+const int y2c_cbu = int(2.018*65536+0.5);
+const int y2c_cgu = int(0.391*65536+0.5);
+const int y2c_cgv = int(0.813*65536+0.5);
+const int y2c_crv = int(1.596*65536+0.5);
+int y2c_bu[256];
+int y2c_gu[256];
+int y2c_gv[256];
+int y2c_rv[256];
+
+const int cy_cy = int(255.0/219.0*65536+0.5);
+const int cy_cy2 = int(255.0/219.0*32768+0.5);
+
+bool fColorConvInitOK = false;
+
+void ColorConvInit()
+{
+	if(fColorConvInitOK) return;
+
+	int i;
+
+	for(i = 0; i < 256; i++) 
+	{
+		Clip_base[i] = 0;
+		Clip_base[i+256] = i;
+		Clip_base[i+512] = 255;
+	}
+
+	for(i = 0; i < 256; i++)
+	{
+		c2y_yb[i] = c2y_cyb*i;
+		c2y_yg[i] = c2y_cyg*i;
+		c2y_yr[i] = c2y_cyr*i;
+
+		y2c_bu[i] = y2c_cbu*(i-128);
+		y2c_gu[i] = y2c_cgu*(i-128);
+		y2c_gv[i] = y2c_cgv*(i-128);
+		y2c_rv[i] = y2c_crv*(i-128);
+	}
+
+	fColorConvInitOK = true;
+}
+
+#define rgb2yuv(r1,g1,b1,r2,g2,b2) \
+	int y1 = (c2y_yb[b1] + c2y_yg[g1] + c2y_yr[r1] + 0x108000) >> 16; \
+	int y2 = (c2y_yb[b2] + c2y_yg[g2] + c2y_yr[r2] + 0x108000) >> 16; \
+\
+	int scaled_y = (y1+y2-32) * cy_cy2; \
+\
+	unsigned char u = Clip[(((((b1+b2)<<15) - scaled_y) >> 10) * c2y_cu + 0x800000 + 0x8000) >> 16]; \
+	unsigned char v = Clip[(((((r1+r2)<<15) - scaled_y) >> 10) * c2y_cv + 0x800000 + 0x8000) >> 16]; \
+
+//
+// CMemSubPic
+//
+
+CMemSubPic::CMemSubPic(SubPicDesc& spd)
+	: m_spd(spd)
+{
+	m_maxsize.SetSize(spd.w, spd.h);
+	m_rcDirty.SetRect(0, 0, spd.w, spd.h);
+}
+
+CMemSubPic::~CMemSubPic()
+{
+	delete [] m_spd.bits, m_spd.bits = NULL;
+}
+
+// ISubPic
+
+STDMETHODIMP_(void*) CMemSubPic::GetObject()
+{
+	return (void*)&m_spd;
+}
+
+STDMETHODIMP CMemSubPic::GetDesc(SubPicDesc& spd)
+{
+	spd.type = m_spd.type;
+	spd.w = m_size.cx;
+	spd.h = m_size.cy;
+	spd.bpp = m_spd.bpp;
+	spd.pitch = m_spd.pitch;
+	spd.bits = m_spd.bits;
+	spd.bitsU = m_spd.bitsU;
+	spd.bitsV = m_spd.bitsV;
+	spd.vidrect = m_vidrect;
+
+	return S_OK;
+}
+
+STDMETHODIMP CMemSubPic::CopyTo(ISubPic* pSubPic)
+{
+	HRESULT hr;
+	if(FAILED(hr = __super::CopyTo(pSubPic)))
+		return hr;
+
+	SubPicDesc src, dst;
+	if(FAILED(GetDesc(src)) || FAILED(pSubPic->GetDesc(dst)))
+		return E_FAIL;
+
+	int w = m_rcDirty.Width(), h = m_rcDirty.Height();
+
+	BYTE* s = (BYTE*)src.bits + src.pitch*m_rcDirty.top + m_rcDirty.left*4;
+	BYTE* d = (BYTE*)dst.bits + dst.pitch*m_rcDirty.top + m_rcDirty.left*4;
+
+	for(int j = 0; j < h; j++, s += src.pitch, d += dst.pitch)
+		memcpy(d, s, w*4);
+
+	return S_OK;
+}
+
+STDMETHODIMP CMemSubPic::ClearDirtyRect(DWORD color)
+{
+	if(m_rcDirty.IsRectEmpty())
+		return S_FALSE;
+
+	BYTE* p = (BYTE*)m_spd.bits + m_spd.pitch*m_rcDirty.top + m_rcDirty.left*(m_spd.bpp>>3);
+	for(int j = 0, h = m_rcDirty.Height(); j < h; j++, p += m_spd.pitch)
+	{
+//        memsetd(p, 0, m_rcDirty.Width());
+
+		int w = m_rcDirty.Width();
+		__asm
+		{
+			mov eax, color
+			mov ecx, w
+			mov edi, p
+			cld
+			rep stosd
+		}
+	}
+	
+	m_rcDirty.SetRectEmpty();
+
+	return S_OK;
+}
+
+STDMETHODIMP CMemSubPic::Lock(SubPicDesc& spd)
+{
+	return GetDesc(spd);
+}
+
+STDMETHODIMP CMemSubPic::Unlock(RECT* pDirtyRect)
+{
+	m_rcDirty = pDirtyRect ? *pDirtyRect : CRect(0,0,m_spd.w,m_spd.h);
+
+	if(m_rcDirty.IsRectEmpty())
+		return S_OK;
+	
+    if(m_spd.type == MSP_YUY2 || m_spd.type == MSP_YV12 || m_spd.type == MSP_IYUV || m_spd.type == MSP_AYUV)
+	{
+		ColorConvInit();
+
+		if(m_spd.type == MSP_YUY2 || m_spd.type == MSP_YV12 || m_spd.type == MSP_IYUV)
+		{
+			m_rcDirty.left &= ~1;
+			m_rcDirty.right = (m_rcDirty.right+1)&~1;
+
+			if(m_spd.type == MSP_YV12 || m_spd.type == MSP_IYUV)
+			{
+				m_rcDirty.top &= ~1;
+				m_rcDirty.bottom = (m_rcDirty.bottom+1)&~1;
+			}
+		}
+	}
+
+	int w = m_rcDirty.Width(), h = m_rcDirty.Height();
+
+	BYTE* top = (BYTE*)m_spd.bits + m_spd.pitch*m_rcDirty.top + m_rcDirty.left*4;
+	BYTE* bottom = top + m_spd.pitch*h;
+
+	if(m_spd.type == MSP_RGB16)
+	{
+		for(; top < bottom ; top += m_spd.pitch)
+		{
+			DWORD* s = (DWORD*)top;
+			DWORD* e = s + w;
+			for(; s < e; s++)
+			{
+				*s = ((*s>>3)&0x1f000000)|((*s>>8)&0xf800)|((*s>>5)&0x07e0)|((*s>>3)&0x001f);
+//				*s = (*s&0xff000000)|((*s>>8)&0xf800)|((*s>>5)&0x07e0)|((*s>>3)&0x001f);
+			}
+		}
+	}
+	else if(m_spd.type == MSP_RGB15)
+	{
+		for(; top < bottom; top += m_spd.pitch)
+		{
+			DWORD* s = (DWORD*)top;
+			DWORD* e = s + w;
+			for(; s < e; s++)
+			{
+				*s = ((*s>>3)&0x1f000000)|((*s>>9)&0x7c00)|((*s>>6)&0x03e0)|((*s>>3)&0x001f);
+//				*s = (*s&0xff000000)|((*s>>9)&0x7c00)|((*s>>6)&0x03e0)|((*s>>3)&0x001f);
+			}
+		}
+	}
+	else if(m_spd.type == MSP_YUY2 || m_spd.type == MSP_YV12 || m_spd.type == MSP_IYUV)
+	{
+		for(; top < bottom ; top += m_spd.pitch)
+		{
+			BYTE* s = top;
+			BYTE* e = s + w*4;
+			for(; s < e; s+=8) // ARGB ARGB -> AxYU AxYV
+			{
+				if((s[3]+s[7]) < 0x1fe)
+				{
+					s[1] = (c2y_yb[s[0]] + c2y_yg[s[1]] + c2y_yr[s[2]] + 0x108000) >> 16;
+					s[5] = (c2y_yb[s[4]] + c2y_yg[s[5]] + c2y_yr[s[6]] + 0x108000) >> 16;
+
+					int scaled_y = (s[1]+s[5]-32) * cy_cy2;
+					
+					s[0] = Clip[(((((s[0]+s[4])<<15) - scaled_y) >> 10) * c2y_cu + 0x800000 + 0x8000) >> 16];
+					s[4] = Clip[(((((s[2]+s[6])<<15) - scaled_y) >> 10) * c2y_cv + 0x800000 + 0x8000) >> 16];
+				}
+				else
+				{
+					s[1] = s[5] = 0x10;
+					s[0] = s[4] = 0x80;
+				}
+			}
+		}
+	}
+	else if(m_spd.type == MSP_AYUV)
+	{
+		for(; top < bottom ; top += m_spd.pitch)
+		{
+			BYTE* s = top;
+			BYTE* e = s + w*4;
+			for(; s < e; s+=4) // ARGB -> AYUV
+			{
+				if(s[3] < 0xff)
+				{
+					int y = (c2y_yb[s[0]] + c2y_yg[s[1]] + c2y_yr[s[2]] + 0x108000) >> 16;
+					int scaled_y = (y-32) * cy_cy;
+					s[1] = Clip[((((s[0]<<16) - scaled_y) >> 10) * c2y_cu + 0x800000 + 0x8000) >> 16];
+					s[0] = Clip[((((s[2]<<16) - scaled_y) >> 10) * c2y_cv + 0x800000 + 0x8000) >> 16];
+					s[2] = y;
+				}
+				else
+				{
+					s[0] = s[1] = 0x80;
+					s[2] = 0x10;
+				}
+			}
+		}
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP CMemSubPic::AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget)
+{
+	ASSERT(pTarget);
+
+	if(!pSrc || !pDst || !pTarget)
+		return E_POINTER;
+
+	const SubPicDesc& src = m_spd;
+	SubPicDesc dst = *pTarget; // copy, because we might modify it
+
+	if(src.type != dst.type)
+		return E_INVALIDARG;
+
+	CRect rs(*pSrc), rd(*pDst);
+
+	if(dst.h < 0)
+	{
+		dst.h = -dst.h;
+		rd.bottom = dst.h - rd.bottom;
+		rd.top = dst.h - rd.top;
+	}
+
+	if(rs.Width() != rd.Width() || rs.Height() != abs(rd.Height()))
+		return E_INVALIDARG;
+
+	int w = rs.Width(), h = rs.Height();
+
+	BYTE* s = (BYTE*)src.bits + src.pitch*rs.top + rs.left*4;
+	BYTE* d = (BYTE*)dst.bits + dst.pitch*rd.top + ((rd.left*dst.bpp)>>3);
+
+	if(rd.top > rd.bottom)
+	{
+		if(dst.type == MSP_RGB32 || dst.type == MSP_RGB24
+		|| dst.type == MSP_RGB16 || dst.type == MSP_RGB15
+		|| dst.type == MSP_YUY2 || dst.type == MSP_AYUV)
+		{
+			d = (BYTE*)dst.bits + dst.pitch*(rd.top-1) + (rd.left*dst.bpp>>3);
+		}
+		else if(dst.type == MSP_YV12 || dst.type == MSP_IYUV)
+		{
+			d = (BYTE*)dst.bits + dst.pitch*(rd.top-1) + (rd.left*8>>3);
+		}
+		else 
+		{
+			return E_NOTIMPL;
+		}
+
+		dst.pitch = -dst.pitch;
+	}
+
+	for(int j = 0; j < h; j++, s += src.pitch, d += dst.pitch)
+	{
+		if(dst.type == MSP_RGB32 || dst.type == MSP_AYUV)
+		{
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			DWORD* d2 = (DWORD*)d;
+			for(; s2 < s2end; s2 += 4, d2++)
+			{
+				if(s2[3] < 0xff)
+				{
+					*d2 = (((((*d2&0x00ff00ff)*s2[3])>>8) + (*((DWORD*)s2)&0x00ff00ff))&0x00ff00ff)
+						| (((((*d2&0x0000ff00)*s2[3])>>8) + (*((DWORD*)s2)&0x0000ff00))&0x0000ff00);
+				}
+			}
+		}
+		else if(dst.type == MSP_RGB24)
+		{
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			BYTE* d2 = d;
+			for(; s2 < s2end; s2 += 4, d2 += 3)
+			{
+				if(s2[3] < 0xff)
+				{
+					d2[0] = ((d2[0]*s2[3])>>8) + s2[0];
+					d2[1] = ((d2[1]*s2[3])>>8) + s2[1];
+					d2[2] = ((d2[2]*s2[3])>>8) + s2[2];
+				}
+			}
+		}
+		else if(dst.type == MSP_RGB16)
+		{
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			WORD* d2 = (WORD*)d;
+			for(; s2 < s2end; s2 += 4, d2++)
+			{
+				if(s2[3] < 0x1f)
+				{
+					
+					*d2 = (WORD)((((((*d2&0xf81f)*s2[3])>>5) + (*(DWORD*)s2&0xf81f))&0xf81f)
+								| (((((*d2&0x07e0)*s2[3])>>5) + (*(DWORD*)s2&0x07e0))&0x07e0));
+/*					*d2 = (WORD)((((((*d2&0xf800)*s2[3])>>8) + (*(DWORD*)s2&0xf800))&0xf800)
+						| (((((*d2&0x07e0)*s2[3])>>8) + (*(DWORD*)s2&0x07e0))&0x07e0)
+						| (((((*d2&0x001f)*s2[3])>>8) + (*(DWORD*)s2&0x001f))&0x001f));
+*/
+				}
+			}
+		}
+		else if(dst.type == MSP_RGB15)
+		{
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			WORD* d2 = (WORD*)d;
+			for(; s2 < s2end; s2 += 4, d2++)
+			{
+				if(s2[3] < 0x1f)
+				{
+					*d2 = (WORD)((((((*d2&0x7c1f)*s2[3])>>5) + (*(DWORD*)s2&0x7c1f))&0x7c1f)
+								| (((((*d2&0x03e0)*s2[3])>>5) + (*(DWORD*)s2&0x03e0))&0x03e0));
+/*					*d2 = (WORD)((((((*d2&0x7c00)*s2[3])>>8) + (*(DWORD*)s2&0x7c00))&0x7c00)
+						| (((((*d2&0x03e0)*s2[3])>>8) + (*(DWORD*)s2&0x03e0))&0x03e0)
+						| (((((*d2&0x001f)*s2[3])>>8) + (*(DWORD*)s2&0x001f))&0x001f));
+*/				}
+			}
+		}
+		else if(dst.type == MSP_YUY2)
+		{
+//			BYTE y1, y2, u, v;
+			unsigned int ia, c;
+
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			DWORD* d2 = (DWORD*)d;
+			for(; s2 < s2end; s2 += 8, d2++)
+			{
+				ia = (s2[3]+s2[7])>>1;
+				if(ia < 0xff)
+				{
+/*					y1 = (BYTE)(((((*d2&0xff)-0x10)*s2[3])>>8) + s2[1]); // + y1;
+					y2 = (BYTE)((((((*d2>>16)&0xff)-0x10)*s2[7])>>8) + s2[5]); // + y2;
+					u = (BYTE)((((((*d2>>8)&0xff)-0x80)*ia)>>8) + s2[0]); // + u;
+					v = (BYTE)((((((*d2>>24)&0xff)-0x80)*ia)>>8) + s2[4]); // + v;
+
+					*d2 = (v<<24)|(y2<<16)|(u<<8)|y1;
+*/
+					static const __int64 _8181 = 0x0080001000800010i64;
+
+					ia = (ia<<24)|(s2[7]<<16)|(ia<<8)|s2[3];
+					c = (s2[4]<<24)|(s2[5]<<16)|(s2[0]<<8)|s2[1]; // (v<<24)|(y2<<16)|(u<<8)|y1;
+
+					__asm
+					{
+						mov			esi, s2
+						mov			edi, d2
+						pxor		mm0, mm0
+						movq		mm1, _8181
+						movd		mm2, c
+						punpcklbw	mm2, mm0
+						movd		mm3, [edi]
+						punpcklbw	mm3, mm0
+						movd		mm4, ia
+						punpcklbw	mm4, mm0
+						psrlw		mm4, 1
+						psubsw		mm3, mm1
+						pmullw		mm3, mm4
+						psraw		mm3, 7
+						paddsw		mm3, mm2
+						packuswb	mm3, mm3
+						movd		[edi], mm3
+					};
+				}
+			}
+		}
+		else if(dst.type == MSP_YV12 || dst.type == MSP_IYUV)
+		{
+			BYTE* s2 = s;
+			BYTE* s2end = s2 + w*4;
+			BYTE* d2 = d;
+			for(; s2 < s2end; s2 += 4, d2++)
+			{
+				if(s2[3] < 0xff)
+				{
+					d2[0] = (((d2[0]-0x10)*s2[3])>>8) + s2[1];
+				}
+			}
+		}
+		else
+		{
+			return E_NOTIMPL;
+		}
+	}
+
+	dst.pitch = abs(dst.pitch);
+
+	if(dst.type == MSP_YV12 || dst.type == MSP_IYUV)
+	{
+		int w2 = w/2, h2 = h/2;
+
+		if(!dst.pitchUV)
+		{
+			dst.pitchUV = dst.pitch/2;
+		}
+
+		int sizep4 = dst.pitchUV*dst.h/2;
+
+		BYTE* ss[2];
+		ss[0] = (BYTE*)src.bits + src.pitch*rs.top + rs.left*4;
+		ss[1] = ss[0] + 4;
+
+		if(!dst.bitsU || !dst.bitsV)
+		{
+			dst.bitsU = (BYTE*)dst.bits + dst.pitch*dst.h;
+			dst.bitsV = dst.bitsU + dst.pitchUV*dst.h/2;
+
+			if(dst.type == MSP_YV12)
+			{
+				BYTE* p = dst.bitsU; 
+				dst.bitsU = dst.bitsV; 
+				dst.bitsV = p;
+			}
+		}
+
+		BYTE* dd[2];
+		dd[0] = dst.bitsU + dst.pitchUV*rd.top/2 + rd.left/2;
+		dd[1] = dst.bitsV + dst.pitchUV*rd.top/2 + rd.left/2;
+
+		if(rd.top > rd.bottom)
+		{
+			dd[0] = dst.bitsU + dst.pitchUV*(rd.top/2-1) + rd.left/2;
+			dd[1] = dst.bitsV + dst.pitchUV*(rd.top/2-1) + rd.left/2;
+			dst.pitchUV = -dst.pitchUV;
+		}
+
+		for(int i = 0; i < 2; i++)
+		{
+			s = ss[i]; d = dd[i];
+			BYTE* is = ss[1-i];
+			for(int j = 0; j < h2; j++, s += src.pitch*2, d += dst.pitchUV, is += src.pitch*2)
+			{
+				BYTE* s2 = s;
+				BYTE* s2end = s2 + w*4;
+				BYTE* d2 = d;
+				BYTE* is2 = is;
+				for(; s2 < s2end; s2 += 8, d2++, is2 += 8)
+				{
+					unsigned int ia = (s2[3]+s2[3+src.pitch]+is2[3]+is2[3+src.pitch])>>2;
+					if(ia < 0xff)
+					{
+						*d2 = (((*d2-0x80)*ia)>>8) + ((s2[0]+s2[src.pitch])>>1);
+					}
+				}
+			}
+		}
+	}
+
+	__asm emms;
+
+    return S_OK;
+}
+
+//
+// CMemSubPicAllocator
+//
+
+CMemSubPicAllocator::CMemSubPicAllocator(int type, SIZE maxsize) 
+	: ISubPicAllocatorImpl(maxsize, false, false)
+	, m_type(type)
+	, m_maxsize(maxsize)
+{
+}
+
+// ISubPicAllocatorImpl
+
+bool CMemSubPicAllocator::Alloc(bool fStatic, ISubPic** ppSubPic)
+{
+	if(!ppSubPic) 
+		return(false);
+
+	SubPicDesc spd;
+	spd.w = m_maxsize.cx;
+	spd.h = m_maxsize.cy;
+	spd.bpp = 32;
+	spd.pitch = (spd.w*spd.bpp)>>3;
+	spd.type = m_type;
+	if(!(spd.bits = new BYTE[spd.pitch*spd.h]))
+		return(false);
+
+	if(!(*ppSubPic = new CMemSubPic(spd)))
+		return(false);
+
+	(*ppSubPic)->AddRef();
+
+	return(true);
+}
diff --git a/vsfilter/subpic/MemSubPic.h b/vsfilter/subpic/MemSubPic.h
new file mode 100644
index 0000000000000000000000000000000000000000..a2fe2f2a4e4fc2f7a2f577359d507d2d035affaf
--- /dev/null
+++ b/vsfilter/subpic/MemSubPic.h
@@ -0,0 +1,62 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "ISubPic.h"
+
+enum {MSP_RGB32,MSP_RGB24,MSP_RGB16,MSP_RGB15,MSP_YUY2,MSP_YV12,MSP_IYUV,MSP_AYUV};
+
+// CMemSubPic
+
+class CMemSubPic : public ISubPicImpl
+{
+	SubPicDesc m_spd;
+
+protected:
+	STDMETHODIMP_(void*) GetObject(); // returns SubPicDesc*
+
+public:
+	CMemSubPic(SubPicDesc& spd);
+	virtual ~CMemSubPic();
+
+	// ISubPic
+	STDMETHODIMP GetDesc(SubPicDesc& spd);
+	STDMETHODIMP CopyTo(ISubPic* pSubPic);
+	STDMETHODIMP ClearDirtyRect(DWORD color);
+	STDMETHODIMP Lock(SubPicDesc& spd);
+	STDMETHODIMP Unlock(RECT* pDirtyRect);
+	STDMETHODIMP AlphaBlt(RECT* pSrc, RECT* pDst, SubPicDesc* pTarget);
+};
+
+// CMemSubPicAllocator
+
+class CMemSubPicAllocator : public ISubPicAllocatorImpl
+{
+	int m_type;
+	CSize m_maxsize;
+
+	bool Alloc(bool fStatic, ISubPic** ppSubPic);
+
+public:
+	CMemSubPicAllocator(int type, SIZE maxsize);
+};
+
diff --git a/vsfilter/subpic/stdafx.cpp b/vsfilter/subpic/stdafx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..febe27952ecdb320a724c8aab2385821e8f4e494
--- /dev/null
+++ b/vsfilter/subpic/stdafx.cpp
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// subpic.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/vsfilter/subpic/stdafx.h b/vsfilter/subpic/stdafx.h
new file mode 100644
index 0000000000000000000000000000000000000000..6725784e71ba064f243be3b2ebd8bbe8d5415a20
--- /dev/null
+++ b/vsfilter/subpic/stdafx.h
@@ -0,0 +1,43 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC core and standard components
+
+// TODO: reference additional headers your program requires here
+
+#include <streams.h>
+#include <dvdmedia.h>
+#include "..\DSUtil\DSUtil.h"
diff --git a/vsfilter/subpic/subpic.vcproj b/vsfilter/subpic/subpic.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..fc86ed72ca89365a8c5dd05a19acac3361dc8f66
--- /dev/null
+++ b/vsfilter/subpic/subpic.vcproj
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="subpic"
+	ProjectGUID="{11B20554-2747-469C-9093-3345B1D99E0C}"
+	RootNamespace="subpic"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="d3d9.lib ddraw.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="d3d9.lib ddraw.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<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=".\CoordGeom.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DX7SubPic.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DX9SubPic.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\ISubPic.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\MemSubPic.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\CoordGeom.h"
+				>
+			</File>
+			<File
+				RelativePath=".\DX7SubPic.h"
+				>
+			</File>
+			<File
+				RelativePath=".\DX9SubPic.h"
+				>
+			</File>
+			<File
+				RelativePath=".\ISubPic.h"
+				>
+			</File>
+			<File
+				RelativePath=".\MemSubPic.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.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/vsfilter/subtitles/CCDecoder.cpp b/vsfilter/subtitles/CCDecoder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b3512ba8978f909721c313b2f14efc14e6aac04
--- /dev/null
+++ b/vsfilter/subtitles/CCDecoder.cpp
@@ -0,0 +1,384 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "StdAfx.h"
+#include "ccdecoder.h"
+
+CCDecoder::CCDecoder(CString fn, CString rawfn) : m_fn(fn), m_rawfn(rawfn)
+{
+	m_sts.CreateDefaultStyle(ANSI_CHARSET);
+
+	m_time = 0;
+	m_fEndOfCaption = false;
+	memset(m_buff, 0, sizeof(m_buff));
+	memset(m_disp, 0, sizeof(m_disp));
+	m_cursor = CPoint(0, 0);
+
+	if(!m_rawfn.IsEmpty()) 
+		_tremove(m_rawfn);
+}
+
+CCDecoder::~CCDecoder()
+{
+	if(!m_sts.IsEmpty() && !m_fn.IsEmpty()) 
+	{
+		m_sts.Sort();
+		m_sts.SaveAs(m_fn, EXTSRT, -1, CTextFile::ASCII);
+		m_sts.SaveAs(m_fn.Left(m_fn.ReverseFind('.')+1) + _T("utf8.srt"), EXTSRT, -1, CTextFile::UTF8);
+		m_sts.SaveAs(m_fn.Left(m_fn.ReverseFind('.')+1) + _T("utf16le.srt"), EXTSRT, -1, CTextFile::LE16);
+		m_sts.SaveAs(m_fn.Left(m_fn.ReverseFind('.')+1) + _T("utf16be.srt"), EXTSRT, -1, CTextFile::BE16);
+	}
+}
+
+void CCDecoder::MoveCursor(int x, int y)
+{
+	m_cursor = CPoint(x, y);
+	if(m_cursor.x < 0) m_cursor.x = 0;
+	if(m_cursor.y < 0) m_cursor.y = 0;
+	if(m_cursor.x >= 32) m_cursor.x = 0, m_cursor.y++;
+	if(m_cursor.y >= 16) m_cursor.y = 0;
+}
+
+void CCDecoder::OffsetCursor(int x, int y)
+{
+	MoveCursor(m_cursor.x + x, m_cursor.y + y);
+}
+
+void CCDecoder::PutChar(WCHAR c)
+{
+	m_buff[m_cursor.y][m_cursor.x] = c;
+	OffsetCursor(1, 0);
+}
+
+void CCDecoder::SaveDisp(__int64 time)
+{
+	CStringW str;
+
+	for(int row = 0; row < 16; row++)
+	{
+		bool fNonEmptyRow = false;
+
+		for(int col = 0; col < 32; col++)
+		{
+			if(m_disp[row][col]) 
+			{
+				CStringW str2(&m_disp[row][col]);
+				if(fNonEmptyRow) str += ' ';
+				str += str2;
+				col += str2.GetLength();
+				fNonEmptyRow = true;
+			}
+		}
+
+		if(fNonEmptyRow) str += '\n';
+	}
+
+	if(str.IsEmpty()) return;
+
+	m_sts.Add(str, true, (int)m_time, (int)time);
+}
+
+void CCDecoder::DecodeCC(BYTE* buff, int len, __int64 time)
+{
+	if(!m_rawfn.IsEmpty())
+	{
+		if(FILE* f = _tfopen(m_rawfn, _T("at")))
+		{
+			_ftprintf(f, _T("%02d:%02d:%02d.%03d\n"), 
+				(int)(time/1000/60/60), 
+				(int)((time/1000/60)%60), 
+				(int)((time/1000)%60), 
+				(int)(time%1000));
+
+			for(int i = 0; i < len; i++)
+			{
+				_ftprintf(f, _T("%02x"), buff[i]);
+				if(i < len-1) _ftprintf(f, _T(" "));
+				if(i > 0 && (i&15)==15) _ftprintf(f, _T("\n"));
+			}
+			if(len > 0) _ftprintf(f, _T("\n\n"));
+			fclose(f);
+		}
+	}
+
+	for(int i = 0; i < len; i++)
+	{
+		BYTE c = buff[i]&0x7f;
+		if(c >= 0x20)
+		{
+			static WCHAR charmap[0x60] = 
+			{
+				' ','!','"','#','$','%','&','\'','(',')','á','+',',','-','.','/',
+				'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
+				'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
+				'P','Q','R','S','T','U','V','W','X','Y','Z','[','é',']','í','ó',
+				'ú','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+				'p','q','r','s','t','u','v','w','x','y','z','ç','÷','N','n','?'
+			};
+
+			PutChar(charmap[c - 0x20]);
+		}
+		else if(buff[i] != 0x80 && i < len-1)
+		{
+			// codes and special characters are supposed to be doubled
+			if(i < len-3 && buff[i] == buff[i+2] && buff[i+1] == buff[i+3])
+ 				i += 2;
+
+			c = buff[i+1]&0x7f;
+			if(buff[i] == 0x91 && c >= 0x20 && c < 0x30) // formating
+			{
+				// TODO
+			}
+			else if(buff[i] == 0x91 && c == 0x39) // transparent space
+			{
+ 				OffsetCursor(1, 0);
+			}
+			else if(buff[i] == 0x91 && c >= 0x30 && c < 0x40) // special characters
+			{
+				static WCHAR charmap[0x10] =
+				{
+					0x00ae, // (r)egistered
+					0x00b0, // degree
+					0x00bd, // 1/2
+					0x00bf, // inverted question mark
+					0x2122, // trade mark
+					0x00a2, // cent
+					0x00a3, // pound
+					0x266a, // music
+					0x00e0, // a`
+					0x00ff, // transparent space, handled above
+					0x00e8, // e`
+					0x00e2, // a^
+					0x00ea, // e^
+					0x00ee, // i^
+					0x00f4, // o^
+					0x00fb, // u^
+				};
+
+				PutChar(charmap[c - 0x30]);
+			}
+			else if(buff[i] == 0x92 && c >= 0x20 && c < 0x40) // extended characters
+			{
+				static WCHAR charmap[0x20] =
+				{
+					0x00c0, // A'
+					0x00c9, // E'
+					0x00d3, // O'
+					0x00da, // U'
+					0x00dc, // U:
+					0x00fc, // u:
+					0x2018, // `
+					0x00a1, // inverted !
+					0x002a, // *
+					0x2019, // '
+					0x002d, // -
+					0x00a9, // (c)opyright
+					0x2120, // SM
+					0x00b7, // . (dot in the middle)
+					0x201c, // inverted "
+					0x201d, // "
+
+					0x00c1, // A`
+					0x00c2, // A^
+					0x00c7, // C,
+					0x00c8, // E`
+					0x00ca, // E^
+					0x00cb, // E:
+					0x00eb, // e:
+					0x00ce, // I^
+					0x00cf, // I:
+					0x00ef, // i:
+					0x00d4, // O^
+					0x00d9, // U`
+					0x00f9, // u`
+					0x00db, // U^
+					0x00ab, // <<
+					0x00bb, // >>
+				};
+
+				PutChar(charmap[c - 0x20]);
+			}
+			else if(buff[i] == 0x13 && c >= 0x20 && c < 0x40) // more extended characters
+			{
+				static WCHAR charmap[0x20] =
+				{
+					0x00c3, // A~
+					0x00e3, // a~
+					0x00cd, // I'
+					0x00cc, // I`
+					0x00ec, // i`
+					0x00d2, // O`
+					0x00f2, // o`
+					0x00d5, // O~
+					0x00f5, // o~
+					0x007b, // {
+					0x007d, // }
+					0x005c, // /* \ */
+					0x005e, // ^
+					0x005f, // _
+					0x00a6, // |
+					0x007e, // ~
+
+					0x00c4, // A:
+					0x00e4, // a:
+					0x00d6, // O:
+					0x00f6, // o:
+					0x00df, // B (ss in german)
+					0x00a5, // Y=
+					0x00a4, // ox
+					0x007c, // |
+					0x00c5, // Ao
+					0x00e5, // ao
+					0x00d8, // O/
+					0x00f8, // o/
+					0x250c, // |-
+					0x2510, // -|
+					0x2514, // |_
+					0x2518, // _|
+				};
+
+				PutChar(charmap[c - 0x20]);
+			}
+			else if(buff[i] == 0x94 && buff[i+1] == 0xae) // Erase Non-displayed [buffer] Memory
+			{
+				memset(m_buff, 0, sizeof(m_buff));
+			}
+			else if(buff[i] == 0x94 && buff[i+1] == 0x20) // Resume Caption Loading
+			{
+				memset(m_buff, 0, sizeof(m_buff));
+			}
+			else if(buff[i] == 0x94 && buff[i+1] == 0x2f) // End Of Caption
+			{
+ 				if(memcmp(m_disp, m_buff, sizeof(m_disp)) != 0)
+ 				{
+ 					if(m_fEndOfCaption)
+ 						SaveDisp(time + (i/2)*1000/30);
+ 
+ 					m_fEndOfCaption = true;
+ 					memcpy(m_disp, m_buff, sizeof(m_disp));
+ 					m_time = time + (i/2)*1000/30;
+ 				}
+			}
+			else if(buff[i] == 0x94 && buff[i+1] == 0x2c) // Erase Displayed Memory
+			{
+				if(m_fEndOfCaption)
+				{
+					m_fEndOfCaption = false;
+ 					SaveDisp(time + (i/2)*1000/30);
+				}
+
+				memset(m_disp, 0, sizeof(m_disp));
+			}
+			else if(buff[i] == 0x97 && (buff[i+1] == 0xa1 || buff[i+1] == 0xa2 || buff[i+1] == 0x23)) // Tab Over
+			{
+				OffsetCursor(buff[i+1]&3, 0);
+			}
+			else if(buff[i] == 0x91 || buff[i] == 0x92 || buff[i] == 0x15 || buff[i] == 0x16 
+				|| buff[i] == 0x97 || buff[i] == 0x10 || buff[i] == 0x13 || buff[i] == 0x94) // curpos, color, underline
+			{
+				int row = 0;
+				switch(buff[i])
+				{
+				default:
+				case 0x91: row = 0; break;
+				case 0x92: row = 2; break;
+				case 0x15: row = 4; break;
+				case 0x16: row = 6; break;
+				case 0x97: row = 8; break;
+				case 0x10: row = 10; break;
+				case 0x13: row = 12; break;
+				case 0x94: row = 14; break;
+				}
+				if(buff[i+1]&0x20) row++;
+
+				int col = buff[i+1]&0xe;
+				if(col == 0 || (col > 0 && !(buff[i+1]&0x10))) col = 0;
+				else col <<= 1;
+
+				MoveCursor(col, row);
+			}
+			else
+			{
+				int iiii = 0;
+			}
+
+			i++;
+		}
+	}
+}
+
+void CCDecoder::ExtractCC(BYTE* buff, int len, __int64 time)
+{
+	for(int i = 0; i < len-9; i++)
+	{
+		if(*(DWORD*)&buff[i] == 0xb2010000 && *(DWORD*)&buff[i+4] == 0xf8014343)
+		{
+			i += 8;
+			int nBytes = buff[i++]&0x3f;
+			if(nBytes > 0)
+			{
+				nBytes = (nBytes+1)&~1;
+
+				BYTE* pData1 = new BYTE[nBytes];
+				BYTE* pData2 = new BYTE[nBytes];
+
+				if(pData1 && pData2)
+				{
+					int nBytes1 = 0, nBytes2 = 0;
+
+					for(int j = 0; j < nBytes && i < 0x800;)
+					{
+						if(buff[i++] == 0xff)
+						{
+							pData1[nBytes1++] = buff[i++];
+							pData1[nBytes1++] = buff[i++];
+						}
+						else i+=2;
+						
+						j++;
+
+						if(j >= nBytes) break;
+
+						if(buff[i++] == 0xff)
+						{
+							pData2[nBytes2++] = buff[i++];
+							pData2[nBytes2++] = buff[i++];
+						}
+						else i+=2;
+
+						j++;
+					}
+
+					if(nBytes1 > 0)
+						DecodeCC(pData1, nBytes1, time);
+
+					if(nBytes2 > 0)
+						DecodeCC(pData2, nBytes2, time);
+				}
+
+				if(pData1) delete [] pData1;
+				if(pData2) delete [] pData2;
+			}
+
+			break;
+		}
+	}
+}
diff --git a/vsfilter/subtitles/CCDecoder.h b/vsfilter/subtitles/CCDecoder.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc6a1c12629fc63b143a4c669b1ffd02df75d0d0
--- /dev/null
+++ b/vsfilter/subtitles/CCDecoder.h
@@ -0,0 +1,48 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "STS.h"
+
+class CCDecoder
+{
+	CSimpleTextSubtitle m_sts;
+	CString m_fn, m_rawfn;
+	__int64 m_time;
+	bool m_fEndOfCaption;
+	WCHAR m_buff[16][33], m_disp[16][33];
+	CPoint m_cursor;
+
+	void SaveDisp(__int64 time);
+	void MoveCursor(int x, int y);
+	void OffsetCursor(int x, int y);
+	void PutChar(WCHAR c);
+
+public:
+	CCDecoder(CString fn = _T(""), CString rawfn = _T(""));
+	virtual ~CCDecoder();
+	void DecodeCC(BYTE* buff, int len, __int64 time);
+	void ExtractCC(BYTE* buff, int len, __int64 time);
+	CSimpleTextSubtitle& GetSTS() {return m_sts;}
+};
+
+
diff --git a/vsfilter/subtitles/GFN.cpp b/vsfilter/subtitles/GFN.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c3d2d1a6075017e32a6bf235fde879ec3162c05
--- /dev/null
+++ b/vsfilter/subtitles/GFN.cpp
@@ -0,0 +1,173 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <io.h>
+#include "TextFile.h"
+#include "GFN.h"
+
+TCHAR* exttypestr[] = 
+{
+	_T("srt"), _T("sub"), _T("smi"), _T("psb"), 
+	_T("ssa"), _T("ass"), _T("idx"), _T("usf"), 
+	_T("xss"), _T("txt"), _T("ssf")
+};
+
+static TCHAR* ext[2][countof(exttypestr)] = 
+{
+	{
+		_T(".srt"), _T(".sub"), _T(".smi"), _T(".psb"), 
+		_T(".ssa"), _T(".ass"), _T(".idx"), _T(".usf"), 
+		_T(".xss"), _T(".txt"), _T(".ssf")
+	},
+	{
+		_T(".*.srt"), _T(".*.sub"), _T(".*.smi"), _T(".*.psb"), 
+		_T(".*.ssa"), _T(".*.ass"), _T(".*.dummyidx"), _T(".*.usf"), 
+		_T(".*.xss"), _T(".*.txt"), _T(".*.ssf")
+	}, 
+};
+
+#define WEBSUBEXT _T(".wse")
+
+static int SubFileCompare(const void* elem1, const void* elem2)
+{
+	return(((SubFile*)elem1)->fn.CompareNoCase(((SubFile*)elem2)->fn));
+}
+
+void GetSubFileNames(CString fn, CAtlArray<CString>& paths, CAtlArray<SubFile>& ret)
+{
+	ret.RemoveAll();
+
+	int extlistnum = countof(ext);
+	int extsubnum = countof(ext[0]);
+
+	fn.Replace('\\', '/');
+
+	bool fWeb = false;
+	{
+//		int i = fn.Find(_T("://"));
+		int i = fn.Find(_T("http://"));
+		if(i > 0) {fn = _T("http") + fn.Mid(i); fWeb = true;}
+	}
+
+	int	l = fn.GetLength(), l2 = l;
+	l2 = fn.ReverseFind('.');
+	l = fn.ReverseFind('/') + 1;
+	if(l2 < l) l2 = l;
+
+	CString orgpath = fn.Left(l);
+	CString title = fn.Mid(l, l2-l);
+	CString filename = title + _T(".nooneexpectsthespanishinquisition");
+
+	if(!fWeb)
+	{
+		// struct _tfinddata_t file, file2;
+		// long hFile, hFile2 = 0;
+
+		WIN32_FIND_DATA wfd, wfd2;
+		HANDLE hFile, hFile2;
+
+		for(int k = 0; k < paths.GetCount(); k++)
+		{
+			CString path = paths[k];
+			path.Replace('\\', '/');
+
+			l = path.GetLength();
+			if(l > 0 && path[l-1] != '/') path += '/';
+
+			if(path.Find(':') == -1 && path.Find(_T("\\\\")) != 0) path = orgpath + path;
+
+			path.Replace(_T("/./"), _T("/"));
+			path.Replace('/', '\\');
+
+			// CAtlList<CString> sl;
+
+			bool fEmpty = true;
+
+			if((hFile = FindFirstFile(path + title + "*", &wfd)) != INVALID_HANDLE_VALUE)
+			{
+				do
+				{
+					if(filename.CompareNoCase(wfd.cFileName) != 0) 
+					{
+						fEmpty = false;
+						// sl.AddTail(path + file.name);
+					}
+				}
+				while(FindNextFile(hFile, &wfd));
+
+				FindClose(hFile);
+			}
+
+			// TODO: use 'sl' in the next step to find files (already a nice speedup as it is now...)
+			if(fEmpty) continue;
+
+			for(int j = 0; j < extlistnum; j++)
+			{
+				for(int i = 0; i < extsubnum; i++)
+				{
+					if((hFile = FindFirstFile(path + title + ext[j][i], &wfd)) != INVALID_HANDLE_VALUE)
+					{
+						do
+						{
+							CString fn = path + wfd.cFileName;
+
+							hFile2 = INVALID_HANDLE_VALUE;
+
+							if(j == 0 || (hFile2 = FindFirstFile(fn.Left(fn.ReverseFind('.')) + _T(".avi"), &wfd2)) == INVALID_HANDLE_VALUE)
+							{
+								SubFile f;
+								f.fn = fn;
+								ret.Add(f);
+							}
+							
+							if(hFile2 != INVALID_HANDLE_VALUE)
+							{
+								FindClose(hFile2);
+							}
+						}
+						while(FindNextFile(hFile, &wfd));
+						
+						FindClose(hFile);
+					}
+				}
+			}
+		}
+	}
+	else if(l > 7)
+	{
+		CWebTextFile wtf; // :)
+		if(wtf.Open(orgpath + title + WEBSUBEXT))
+		{
+			CString fn;
+			while(wtf.ReadString(fn) && fn.Find(_T("://")) >= 0)
+			{
+				SubFile f;
+				f.fn = fn;
+				ret.Add(f);
+			}
+		}
+	}
+
+	// sort files, this way the user can define the order (movie.00.English.srt, movie.01.Hungarian.srt, etc)
+
+	qsort(ret.GetData(), ret.GetCount(), sizeof(SubFile), SubFileCompare);
+}
diff --git a/vsfilter/subtitles/GFN.h b/vsfilter/subtitles/GFN.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ebedf58cc143af1e5055cc227d98cee5cfd3c2b
--- /dev/null
+++ b/vsfilter/subtitles/GFN.h
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+
+enum exttype {EXTSRT = 0, EXTSUB, EXTSMI, EXTPSB, EXTSSA, EXTASS, EXTIDX, EXTUSF, EXTXSS};
+extern TCHAR* exttypestr[];
+typedef struct {CString fn; /*exttype ext;*/} SubFile;
+extern void GetSubFileNames(CString fn, CAtlArray<CString>& paths, CAtlArray<SubFile>& ret);
diff --git a/vsfilter/subtitles/RTS.cpp b/vsfilter/subtitles/RTS.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..96a77c6c01825a7b906bf2e74c13d86eeefd6388
--- /dev/null
+++ b/vsfilter/subtitles/RTS.cpp
@@ -0,0 +1,2496 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <math.h>
+#include <time.h>
+#include "RTS.h"
+
+// WARNING: this isn't very thread safe, use only one RTS a time.
+static HDC g_hDC;
+static int g_hDC_refcnt = 0;
+
+static long revcolor(long c)
+{
+	return ((c&0xff0000)>>16) + (c&0xff00) + ((c&0xff)<<16);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+// CMyFont
+
+CMyFont::CMyFont(STSStyle& style)
+{
+	LOGFONT lf;
+	memset(&lf, 0, sizeof(lf));
+	lf <<= style;
+	//lf.lfHeight = (LONG)(style.fontSize+0.5);
+	lf.lfOutPrecision = OUT_TT_PRECIS;
+	lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+	lf.lfQuality = ANTIALIASED_QUALITY;
+	lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;
+
+	if(!CreateFontIndirect(&lf))
+	{
+		_tcscpy(lf.lfFaceName, _T("Arial"));
+		CreateFontIndirect(&lf);
+	}
+
+	HFONT hOldFont = SelectFont(g_hDC, *this);
+	TEXTMETRIC tm;
+	GetTextMetrics(g_hDC, &tm);
+	m_ascent = ((tm.tmAscent + 4) >> 3);
+	m_descent = ((tm.tmDescent + 4) >> 3);
+	SelectFont(g_hDC, hOldFont);
+}
+
+// CWord
+
+CWord::CWord(STSStyle& style, CStringW str, int ktype, int kstart, int kend) 
+	: m_style(style), m_str(str)
+	, m_width(0), m_ascent(0), m_descent(0)
+	, m_ktype(ktype), m_kstart(kstart), m_kend(kend)
+	, m_fDrawn(false), m_p(INT_MAX, INT_MAX)
+	, m_fLineBreak(false), m_fWhiteSpaceChar(false)
+	, m_pOpaqueBox(NULL)
+{
+	if(str.IsEmpty()) 
+	{
+		m_fWhiteSpaceChar = m_fLineBreak = true; 
+	}
+
+	CMyFont font(m_style);
+	m_ascent = (int)(m_style.fontScaleY/100*font.m_ascent);
+	m_descent = (int)(m_style.fontScaleY/100*font.m_descent);
+	m_width = 0;
+}
+
+CWord::~CWord()
+{
+	if(m_pOpaqueBox) delete m_pOpaqueBox;
+}
+
+bool CWord::Append(CWord* w)
+{
+	if(!(m_style == w->m_style)
+	|| m_fLineBreak || w->m_fLineBreak
+	|| w->m_kstart != w->m_kend || m_ktype != w->m_ktype) return(false);
+
+	m_fWhiteSpaceChar = m_fWhiteSpaceChar && w->m_fWhiteSpaceChar;
+	m_str += w->m_str;
+	m_width += w->m_width;
+
+	m_fDrawn = false;
+	m_p = CPoint(INT_MAX, INT_MAX);
+
+	return(true);
+}
+
+void CWord::Paint(CPoint p, CPoint org)
+{
+	if(!m_str) return;
+
+	if(!m_fDrawn)
+	{
+		if(!CreatePath()) return;
+
+		Transform(CPoint((org.x-p.x)*8, (org.y-p.y)*8));
+
+		if(!ScanConvert()) return;
+
+		if(m_style.borderStyle == 0 && m_style.outlineWidth > 0)
+		{
+			if(!CreateWidenedRegion((int)(m_style.outlineWidth+0.5))) return;
+		}
+		else if(m_style.borderStyle == 1)
+		{
+			if(!CreateOpaqueBox()) return;
+		}
+
+		m_fDrawn = true;
+
+		if(!Rasterize(p.x&7, p.y&7, m_style.fBlur)) return;
+	}
+	else if((m_p.x&7) != (p.x&7) || (m_p.y&7) != (p.y&7))
+	{
+		Rasterize(p.x&7, p.y&7, m_style.fBlur);
+	}
+
+	m_p = p;
+
+	if(m_pOpaqueBox)
+		m_pOpaqueBox->Paint(p, org);
+}
+
+void CWord::Transform(CPoint org)
+{
+	double scalex = m_style.fontScaleX/100;
+	double scaley = m_style.fontScaleY/100;
+
+	double caz = cos((3.1415/180)*m_style.fontAngleZ);
+	double saz = sin((3.1415/180)*m_style.fontAngleZ);
+	double cax = cos((3.1415/180)*m_style.fontAngleX);
+	double sax = sin((3.1415/180)*m_style.fontAngleX);
+	double cay = cos((3.1415/180)*m_style.fontAngleY);
+	double say = sin((3.1415/180)*m_style.fontAngleY);
+
+	for(int i = 0; i < mPathPoints; i++)
+	{
+		double x, y, z, xx, yy, zz;
+
+		x = scalex * (mpPathPoints[i].x + m_style.fontShiftX * mpPathPoints[i].y) - org.x;
+		y = scaley * (mpPathPoints[i].y + m_style.fontShiftY * mpPathPoints[i].x) - org.y;
+		z = 0;
+
+		xx = x*caz + y*saz;
+		yy = -(x*saz - y*caz);
+		zz = z;
+
+		x = xx;
+		y = yy*cax + zz*sax;
+		z = yy*sax - zz*cax;
+
+		xx = x*cay + z*say;
+		yy = y;
+		zz = x*say - z*cay;
+
+		zz = max(zz, -19000);
+
+		x = (xx * 20000) / (zz + 20000);
+		y = (yy * 20000) / (zz + 20000);
+
+		mpPathPoints[i].x = (LONG)(x + org.x + 0.5);
+		mpPathPoints[i].y = (LONG)(y + org.y + 0.5);
+	}
+}
+
+bool CWord::CreateOpaqueBox()
+{
+	if(m_pOpaqueBox) return(true);
+
+	STSStyle style = m_style;
+	style.borderStyle = 0;
+	style.outlineWidth = 0;
+	style.colors[0] = m_style.colors[2];
+	style.alpha[0] = m_style.alpha[2];
+
+	int w = (int)(m_style.outlineWidth + 0.5);
+
+	CStringW str;
+	str.Format(L"m %d %d l %d %d %d %d %d %d", 
+		-w, -w, 
+		m_width+w, -w, 
+		m_width+w, m_ascent+m_descent+w, 
+		-w, m_ascent+m_descent+w);
+
+	m_pOpaqueBox = new CPolygon(style, str, 0, 0, 0, 1.0/8, 1.0/8, 0);
+
+	return(!!m_pOpaqueBox);
+}
+
+// CText
+
+CText::CText(STSStyle& style, CStringW str, int ktype, int kstart, int kend)
+	: CWord(style, str, ktype, kstart, kend)
+{
+	if(m_str == L" ")
+	{
+		m_fWhiteSpaceChar = true;
+	}
+
+	CMyFont font(m_style);
+
+	HFONT hOldFont = SelectFont(g_hDC, font);
+
+	if(m_style.fontSpacing || (long)GetVersion() < 0)
+	{
+		bool bFirstPath = true;
+
+		for(LPCWSTR s = m_str; *s; s++)
+		{
+			CSize extent;
+			if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
+			m_width += extent.cx + (int)m_style.fontSpacing;
+		}
+//			m_width -= (int)m_style.fontSpacing; // TODO: subtract only at the end of the line
+	}
+	else
+	{
+		CSize extent;
+		if(!GetTextExtentPoint32W(g_hDC, m_str, wcslen(str), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return;}
+		m_width += extent.cx;
+	}
+
+	m_width = (int)(m_style.fontScaleX/100*m_width + 4) >> 3;
+
+	SelectFont(g_hDC, hOldFont);
+}
+
+CWord* CText::Copy()
+{
+	return(new CText(m_style, m_str, m_ktype, m_kstart, m_kend));
+}
+
+bool CText::Append(CWord* w)
+{
+	return(dynamic_cast<CText*>(w) && CWord::Append(w));
+}
+
+bool CText::CreatePath()
+{
+	CMyFont font(m_style);
+
+	HFONT hOldFont = SelectFont(g_hDC, font);
+
+	int width = 0;
+
+	if(m_style.fontSpacing || (long)GetVersion() < 0)
+	{
+		bool bFirstPath = true;
+
+		for(LPCWSTR s = m_str; *s; s++)
+		{
+			CSize extent;
+			if(!GetTextExtentPoint32W(g_hDC, s, 1, &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
+
+			PartialBeginPath(g_hDC, bFirstPath); bFirstPath = false;
+			TextOutW(g_hDC, 0, 0, s, 1);
+			PartialEndPath(g_hDC, width, 0);
+
+			width += extent.cx + (int)m_style.fontSpacing;
+		}
+	}
+	else
+	{
+		CSize extent;
+		if(!GetTextExtentPoint32W(g_hDC, m_str, m_str.GetLength(), &extent)) {SelectFont(g_hDC, hOldFont); ASSERT(0); return(false);}
+
+		BeginPath(g_hDC);
+		TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
+		EndPath(g_hDC);
+	}
+
+	SelectFont(g_hDC, hOldFont);
+
+	return(true);
+}
+
+// CPolygon
+
+CPolygon::CPolygon(STSStyle& style, CStringW str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline) 
+	: CWord(style, str, ktype, kstart, kend)
+	, m_scalex(scalex), m_scaley(scaley), m_baseline(baseline)
+{
+	ParseStr();
+}
+
+CPolygon::~CPolygon()
+{
+}
+
+CWord* CPolygon::Copy()
+{
+	return(new CPolygon(m_style, m_str, m_ktype, m_kstart, m_kend, m_scalex, m_scaley, m_baseline));
+}
+
+bool CPolygon::Append(CWord* w)
+{
+	int width = m_width;
+
+	CPolygon* p = dynamic_cast<CPolygon*>(w);
+	if(!p) return(false);
+
+	// TODO
+	return(false);
+
+	return(true);
+}
+
+bool CPolygon::GetLONG(CStringW& str, LONG& ret)
+{
+	LPWSTR s = (LPWSTR)(LPCWSTR)str, e = s;
+	ret = wcstol(str, &e, 10);
+	str = str.Mid(e - s);
+	return(e > s);
+}
+
+bool CPolygon::GetPOINT(CStringW& str, POINT& ret)
+{
+	return(GetLONG(str, ret.x) && GetLONG(str, ret.y));
+}
+
+bool CPolygon::ParseStr()
+{
+	if(m_pathTypesOrg.GetCount() > 0) return(true);
+
+	CPoint p;
+	int i, j, lastsplinestart = -1, firstmoveto = -1, lastmoveto = -1;
+
+	CStringW str = m_str;
+	str.SpanIncluding(L"mnlbspc 0123456789");
+	str.Replace(L"m", L"*m");
+	str.Replace(L"n", L"*n");
+	str.Replace(L"l", L"*l");
+	str.Replace(L"b", L"*b");
+	str.Replace(L"s", L"*s");
+	str.Replace(L"p", L"*p");
+	str.Replace(L"c", L"*c");
+
+	int k = 0;
+	for(CStringW s = str.Tokenize(L"*", k); !s.IsEmpty(); s = str.Tokenize(L"*", k))
+	{
+		WCHAR c = s[0];
+		s.TrimLeft(L"mnlbspc ");
+		switch(c)
+		{
+		case 'm': 
+			lastmoveto = m_pathTypesOrg.GetCount();
+			if(firstmoveto == -1) firstmoveto = lastmoveto;
+			while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
+			break;
+		case 'n':
+			while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
+			break;
+		case 'l':
+			while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
+			break;
+		case 'b':
+			j = m_pathTypesOrg.GetCount();
+			while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
+			j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
+			m_pathTypesOrg.SetCount(j); m_pathPointsOrg.SetCount(j);
+			break;
+		case 's':
+			j = lastsplinestart = m_pathTypesOrg.GetCount();
+			i = 3;
+			while(i-- && GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
+			if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
+			// no break here
+		case 'p':
+			while(GetPOINT(s, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
+			break;
+		case 'c':
+			if(lastsplinestart > 0)
+			{
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				p = m_pathPointsOrg[lastsplinestart-1]; // we need p for temp storage, because operator [] will return a reference to CPoint and Add() may reallocate its internal buffer (this is true for MFC 7.0 but not for 6.0, hehe)
+				m_pathPointsOrg.Add(p);
+				p = m_pathPointsOrg[lastsplinestart];
+				m_pathPointsOrg.Add(p);
+				p = m_pathPointsOrg[lastsplinestart+1];
+				m_pathPointsOrg.Add(p);
+				lastsplinestart = -1;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+/*
+	LPCWSTR str = m_str;
+	while(*str)
+	{
+		while(*str && *str != 'm' && *str != 'n' && *str != 'l' && *str != 'b' && *str != 's' && *str != 'p' && *str != 'c') str++;
+
+		if(!*str) break;
+
+		switch(*str++)
+		{
+		case 'm': 
+			lastmoveto = m_pathTypesOrg.GetCount();
+			if(firstmoveto == -1) firstmoveto = lastmoveto;
+			while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETO); m_pathPointsOrg.Add(p);}
+			break;
+		case 'n':
+			while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_MOVETONC); m_pathPointsOrg.Add(p);}
+			break;
+		case 'l':
+			while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_LINETO); m_pathPointsOrg.Add(p);}
+			break;
+		case 'b':
+			j = m_pathTypesOrg.GetCount();
+			while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BEZIERTO); m_pathPointsOrg.Add(p); j++;}
+			j = m_pathTypesOrg.GetCount() - ((m_pathTypesOrg.GetCount()-j)%3);
+			m_pathTypesOrg.SetCount(j); m_pathPointsOrg.SetCount(j);
+			break;
+		case 's':
+			j = lastsplinestart = m_pathTypesOrg.GetCount();
+			i = 3;
+			while(i-- && GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINETO); m_pathPointsOrg.Add(p); j++;}
+			if(m_pathTypesOrg.GetCount()-lastsplinestart < 3) {m_pathTypesOrg.SetCount(lastsplinestart); m_pathPointsOrg.SetCount(lastsplinestart); lastsplinestart = -1;}
+			// no break here
+		case 'p':
+			while(GetPOINT(str, p)) {m_pathTypesOrg.Add(PT_BSPLINEPATCHTO); m_pathPointsOrg.Add(p); j++;}
+			break;
+		case 'c':
+			if(lastsplinestart > 0)
+			{
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				m_pathTypesOrg.Add(PT_BSPLINEPATCHTO);
+				p = m_pathPointsOrg[lastsplinestart-1]; // we need p for temp storage, because operator [] will return a reference to CPoint and Add() may reallocate its internal buffer (this is true for MFC 7.0 but not for 6.0, hehe)
+				m_pathPointsOrg.Add(p);
+				p = m_pathPointsOrg[lastsplinestart];
+				m_pathPointsOrg.Add(p);
+				p = m_pathPointsOrg[lastsplinestart+1];
+				m_pathPointsOrg.Add(p);
+				lastsplinestart = -1;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if(firstmoveto > 0) break;
+	}
+*/
+	if(lastmoveto == -1 || firstmoveto > 0) 
+	{
+		m_pathTypesOrg.RemoveAll();
+		m_pathPointsOrg.RemoveAll();
+		return(false);
+	}
+
+	int minx = INT_MAX, miny = INT_MAX, maxx = -INT_MAX, maxy = -INT_MAX;
+
+	for(i = 0; i < m_pathTypesOrg.GetCount(); i++)
+	{
+		m_pathPointsOrg[i].x = (int)(64 * m_scalex * m_pathPointsOrg[i].x);
+		m_pathPointsOrg[i].y = (int)(64 * m_scaley * m_pathPointsOrg[i].y);
+		if(minx > m_pathPointsOrg[i].x) minx = m_pathPointsOrg[i].x;
+		if(miny > m_pathPointsOrg[i].y) miny = m_pathPointsOrg[i].y;
+		if(maxx < m_pathPointsOrg[i].x) maxx = m_pathPointsOrg[i].x;
+		if(maxy < m_pathPointsOrg[i].y) maxy = m_pathPointsOrg[i].y;
+	}
+
+	m_width = max(maxx - minx, 0);
+	m_ascent = max(maxy - miny, 0);
+
+	int baseline = (int)(64 * m_scaley * m_baseline);
+	m_descent = baseline;
+	m_ascent -= baseline;
+
+	m_width = ((int)(m_style.fontScaleX/100 * m_width) + 4) >> 3;
+	m_ascent = ((int)(m_style.fontScaleY/100 * m_ascent) + 4) >> 3;
+	m_descent = ((int)(m_style.fontScaleY/100 * m_descent) + 4) >> 3;
+
+	return(true);
+}
+
+bool CPolygon::CreatePath()
+{
+	int len = m_pathTypesOrg.GetCount();
+	if(len == 0) return(false);
+
+	if(mPathPoints != len)
+	{
+		mpPathTypes = (BYTE*)realloc(mpPathTypes, len*sizeof(BYTE));
+		mpPathPoints = (POINT*)realloc(mpPathPoints, len*sizeof(POINT));
+		if(!mpPathTypes || !mpPathPoints) return(false);
+		mPathPoints = len;
+	}
+
+	memcpy(mpPathTypes, m_pathTypesOrg.GetData(), len*sizeof(BYTE));
+	memcpy(mpPathPoints, m_pathPointsOrg.GetData(), len*sizeof(POINT));
+
+	return(true);
+}
+
+// CClipper
+
+CClipper::CClipper(CStringW str, CSize size, double scalex, double scaley) 
+	: CPolygon(STSStyle(), str, 0, 0, 0, scalex, scaley, 0)
+{
+	m_size.cx = m_size.cy = 0;
+	m_pAlphaMask = NULL;
+
+	if(size.cx < 0 || size.cy < 0 || !(m_pAlphaMask = new BYTE[size.cx*size.cy])) return;
+
+	m_size = size;
+
+	memset(m_pAlphaMask, 0, size.cx*size.cy);
+
+	Paint(CPoint(0, 0), CPoint(0, 0));
+
+	int w = mOverlayWidth, h = mOverlayHeight;
+
+	int x = (mOffsetX+4)>>3, y = (mOffsetY+4)>>3;
+	int xo = 0, yo = 0;
+
+	if(x < 0) {xo = -x; w -= -x; x = 0;}
+	if(y < 0) {yo = -y; h -= -y; y = 0;}
+	if(x+w > m_size.cx) w = m_size.cx-x;
+	if(y+h > m_size.cy) h = m_size.cy-y;
+
+	if(w <= 0 || h <= 0) return;
+
+	const BYTE* src = mpOverlayBuffer + 2*(mOverlayWidth * yo + xo);
+	BYTE* dst = m_pAlphaMask + m_size.cx * y + x;
+
+	while(h--)
+	{
+		for(int wt=0; wt<w; ++wt)
+			dst[wt] = src[wt*2];
+
+		src += 2*mOverlayWidth;
+		dst += m_size.cx;
+	}
+}
+
+CClipper::~CClipper()
+{
+	if(m_pAlphaMask) delete [] m_pAlphaMask;
+	m_pAlphaMask = NULL;
+}
+
+CWord* CClipper::Copy()
+{
+	return(new CClipper(m_str, m_size, m_scalex, m_scaley));
+}
+
+bool CClipper::Append(CWord* w)
+{
+	return(false);
+}
+
+// CLine
+
+CLine::~CLine()
+{
+	POSITION pos = GetHeadPosition();
+	while(pos) delete GetNext(pos);
+}
+
+void CLine::Compact()
+{
+	POSITION pos = GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = GetNext(pos);
+		if(!w->m_fWhiteSpaceChar) break;
+
+		m_width -= w->m_width;
+		delete w;
+		RemoveHead();
+	}
+
+	pos = GetTailPosition();
+	while(pos)
+	{
+		CWord* w = GetPrev(pos);
+		if(!w->m_fWhiteSpaceChar) break;
+
+		m_width -= w->m_width;
+		delete w;
+		RemoveTail();
+	}
+
+	if(IsEmpty()) return;
+
+	CLine l;
+	l.AddTailList(this);
+	RemoveAll();
+
+	CWord* last = NULL;
+
+	pos = l.GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = l.GetNext(pos);
+
+		if(!last || !last->Append(w))
+			AddTail(last = w->Copy());
+	}
+
+	m_ascent = m_descent = m_border = 0;
+
+	pos = GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = GetNext(pos);
+
+		if(m_ascent < w->m_ascent) m_ascent = w->m_ascent;
+		if(m_descent < w->m_descent) m_descent = w->m_descent;
+		if(m_border < w->m_style.outlineWidth) m_border = (int)(w->m_style.outlineWidth+0.5);
+	}
+}
+
+CRect CLine::PaintShadow(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
+{
+	CRect bbox(0, 0, 0, 0);
+
+	POSITION pos = GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = GetNext(pos);
+
+		if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
+
+		if(w->m_style.shadowDepth > 0)
+		{
+			int x = p.x + (int)(w->m_style.shadowDepth+0.5);
+			int y = p.y + m_ascent - w->m_ascent + (int)(w->m_style.shadowDepth+0.5);
+
+			DWORD a = 0xff - w->m_style.alpha[3];
+			if(alpha > 0) a = MulDiv(a, 0xff - alpha, 0xff);
+			COLORREF shadow = revcolor(w->m_style.colors[3]) | (a<<24);
+			long sw[6] = {shadow, -1};
+
+			w->Paint(CPoint(x, y), org);
+
+			if(w->m_style.borderStyle == 0)
+			{
+				bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, 
+					w->m_ktype > 0 || w->m_style.alpha[0] < 0xff, 
+					w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart));
+			}
+			else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
+			{
+				bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
+			}
+		}
+
+		p.x += w->m_width;
+	}
+
+	return(bbox);
+}
+
+CRect CLine::PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
+{
+	CRect bbox(0, 0, 0, 0);
+
+	POSITION pos = GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = GetNext(pos);
+
+		if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
+
+//		if((w->m_style.outlineWidth > 0 || w->m_style.borderStyle == 1 && w->m_style.outlineWidth == 0) && !(w->m_ktype == 2 && time < w->m_kstart))
+		if(w->m_style.outlineWidth > 0 && !(w->m_ktype == 2 && time < w->m_kstart))
+		{
+			int x = p.x;
+			int y = p.y + m_ascent - w->m_ascent;
+
+			DWORD aoutline = w->m_style.alpha[2];
+			if(alpha > 0) aoutline += MulDiv(alpha, 0xff - w->m_style.alpha[2], 0xff);
+			COLORREF outline = revcolor(w->m_style.colors[2]) | ((0xff-aoutline)<<24);
+			long sw[6] = {outline, -1};
+
+			w->Paint(CPoint(x, y), org);
+
+			if(w->m_style.borderStyle == 0)
+			{
+				bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, !w->m_style.alpha[0] && !w->m_style.alpha[1], true);
+			}
+			else if(w->m_style.borderStyle == 1 && w->m_pOpaqueBox)
+			{
+				bbox |= w->m_pOpaqueBox->Draw(spd, clipRect, pAlphaMask, x, y, sw, true/*!w->m_style.alpha[0] && !w->m_style.alpha[1]*/, false);
+			}
+		}
+
+		p.x += w->m_width;
+	}
+
+	return(bbox);
+}
+
+CRect CLine::PaintBody(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha)
+{
+	CRect bbox(0, 0, 0, 0);
+
+	POSITION pos = GetHeadPosition();
+	while(pos)
+	{
+		CWord* w = GetNext(pos);
+
+		if(w->m_fLineBreak) return(bbox); // should not happen since this class is just a line of text without any breaks
+
+		int x = p.x;
+		int y = p.y + m_ascent - w->m_ascent;
+
+		// colors
+
+		DWORD aprimary = w->m_style.alpha[0];
+		if(alpha > 0) aprimary += MulDiv(alpha, 0xff - w->m_style.alpha[0], 0xff);
+		COLORREF primary = revcolor(w->m_style.colors[0]) | ((0xff-aprimary)<<24);
+
+		DWORD asecondary = w->m_style.alpha[1];
+		if(alpha > 0) asecondary += MulDiv(alpha, 0xff - w->m_style.alpha[1], 0xff);
+		COLORREF secondary = revcolor(w->m_style.colors[1]) | ((0xff-asecondary)<<24);
+
+		long sw[6] = {primary, 0, secondary};
+
+		// karaoke
+
+		double t;
+
+		if(w->m_ktype == 0 || w->m_ktype == 2)
+		{
+			t = time < w->m_kstart ? 0 : 1;
+		}
+		else if(w->m_ktype == 1)
+		{
+			if(time < w->m_kstart) t = 0;
+			else if(time < w->m_kend) 
+			{
+				t = 1.0 * (time - w->m_kstart) / (w->m_kend - w->m_kstart);
+
+				double angle = fmod(w->m_style.fontAngleZ, 360.0);
+				if(angle > 90 && angle < 270) 
+				{
+					t = 1-t;
+					COLORREF tmp = sw[0]; sw[0] = sw[2]; sw[2] = tmp;
+				}
+			}
+			else t = 1.0;
+		}
+
+		if(t >= 1)
+		{
+			sw[1] = 0xffffffff;
+		}
+
+		sw[3] = (int)(w->m_style.outlineWidth + t*w->m_width) >> 3;
+		sw[4] = sw[2];
+		sw[5] = 0x00ffffff;
+
+		w->Paint(CPoint(x, y), org);
+
+		bbox |= w->Draw(spd, clipRect, pAlphaMask, x, y, sw, true, false);
+
+		p.x += w->m_width;
+	}
+
+	return(bbox);
+}
+
+
+// CSubtitle
+
+CSubtitle::CSubtitle()
+{
+	memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
+	m_pClipper = NULL;
+	m_scalex = m_scaley = 1;
+}
+
+CSubtitle::~CSubtitle()
+{
+	Empty();
+}
+
+void CSubtitle::Empty()
+{
+	POSITION pos = GetHeadPosition();
+	while(pos) delete GetNext(pos);
+
+	pos = m_words.GetHeadPosition();
+	while(pos) delete m_words.GetNext(pos);
+
+	for(int i = 0; i < EF_NUMBEROFEFFECTS; i++) {if(m_effects[i]) delete m_effects[i];}
+	memset(m_effects, 0, sizeof(Effect*)*EF_NUMBEROFEFFECTS);
+
+	if(m_pClipper) delete m_pClipper;
+	m_pClipper = NULL;
+}
+
+int CSubtitle::GetFullWidth()
+{
+	int width = 0;
+
+	POSITION pos = m_words.GetHeadPosition();
+	while(pos) width += m_words.GetNext(pos)->m_width;
+
+	return(width);
+}
+
+int CSubtitle::GetFullLineWidth(POSITION pos)
+{
+	int width = 0;
+
+	while(pos) 
+	{
+		CWord* w = m_words.GetNext(pos);
+		if(w->m_fLineBreak) break;
+		width += w->m_width;
+	}
+
+	return(width);
+}
+
+int CSubtitle::GetWrapWidth(POSITION pos, int maxwidth)
+{
+	if(m_wrapStyle == 0 || m_wrapStyle == 3)
+	{
+		if(maxwidth > 0) 
+		{
+//			int fullwidth = GetFullWidth();
+			int fullwidth = GetFullLineWidth(pos);
+
+			int minwidth = fullwidth / ((abs(fullwidth) / maxwidth) + 1);
+
+			int width = 0, wordwidth = 0;
+
+			while(pos && width < minwidth)
+			{
+				CWord* w = m_words.GetNext(pos);
+				wordwidth = w->m_width;
+				if(abs(width + wordwidth) < abs(maxwidth)) width += wordwidth;
+			}
+
+			maxwidth = width;
+
+			if(m_wrapStyle == 3 && pos) maxwidth -= wordwidth;
+		}
+	}
+	else if(m_wrapStyle == 1)
+	{
+//		maxwidth = maxwidth;
+	}
+	else if(m_wrapStyle == 2)
+	{
+		maxwidth = INT_MAX;
+	}
+
+	return(maxwidth);
+}
+
+CLine* CSubtitle::GetNextLine(POSITION& pos, int maxwidth)
+{
+	if(pos == NULL) return(NULL);
+
+	CLine* ret = new CLine();
+	if(!ret) return(NULL);
+
+	ret->m_width = ret->m_ascent = ret->m_descent = ret->m_border = 0;
+
+	maxwidth = GetWrapWidth(pos, maxwidth);
+
+	bool fEmptyLine = true;
+
+	while(pos)
+	{
+		CWord* w = m_words.GetNext(pos);
+
+		if(ret->m_ascent < w->m_ascent) ret->m_ascent = w->m_ascent;
+		if(ret->m_descent < w->m_descent) ret->m_descent = w->m_descent;
+		if(ret->m_border < w->m_style.outlineWidth) ret->m_border = (int)(w->m_style.outlineWidth+0.5);
+
+		if(w->m_fLineBreak) 
+		{
+			if(fEmptyLine) {ret->m_ascent /= 2; ret->m_descent /= 2; ret->m_border = 0;}
+
+			ret->Compact();
+
+			return(ret);
+		}
+
+		fEmptyLine = false;
+
+		bool fWSC = w->m_fWhiteSpaceChar;
+
+		int width = w->m_width;
+		POSITION pos2 = pos;
+		while(pos2)
+		{
+			if(m_words.GetAt(pos2)->m_fWhiteSpaceChar != fWSC
+			|| m_words.GetAt(pos2)->m_fLineBreak) break;
+
+			CWord* w2 = m_words.GetNext(pos2);
+			width += w2->m_width;
+		}
+
+		if((ret->m_width += width) <= maxwidth || ret->IsEmpty()) 
+		{
+			ret->AddTail(w->Copy());
+			
+			while(pos != pos2)
+			{
+				ret->AddTail(m_words.GetNext(pos)->Copy());
+			}
+
+			pos = pos2;
+		}
+		else
+		{
+			if(pos) m_words.GetPrev(pos);
+			else pos = m_words.GetTailPosition();
+
+			ret->m_width -= width;
+
+			break;
+		}
+	}
+
+	ret->Compact();
+
+	return(ret);
+}
+
+void CSubtitle::CreateClippers(CSize size)
+{
+	size.cx >>= 3;
+	size.cy >>= 3;
+
+	if(m_effects[EF_BANNER] && m_effects[EF_BANNER]->param[2])
+	{
+		int width = m_effects[EF_BANNER]->param[2];
+
+		int w = size.cx, h = size.cy;
+
+		if(!m_pClipper) 
+		{
+			CStringW str;
+			str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
+			m_pClipper = new CClipper(str, size, 1, 1);
+			if(!m_pClipper) return;
+		}
+
+		int da = (64<<8)/width;
+		BYTE* am = m_pClipper->m_pAlphaMask;
+
+		for(int j = 0; j < h; j++, am += w)
+		{
+			int a = 0;
+			int k = min(width, w);
+			
+			for(int i = 0; i < k; i++, a += da)
+				am[i] = (am[i]*a)>>14;
+
+			a = 0x40<<8;
+			k = w-width;
+
+			if(k < 0) {a -= -k*da; k = 0;}
+            
+			for(int i = k; i < w; i++, a -= da)
+				am[i] = (am[i]*a)>>14;
+		}
+	}
+	else if(m_effects[EF_SCROLL] && m_effects[EF_SCROLL]->param[4])
+	{
+		int height = m_effects[EF_SCROLL]->param[4];
+
+		int w = size.cx, h = size.cy;
+
+		if(!m_pClipper) 
+		{
+			CStringW str;
+			str.Format(L"m %d %d l %d %d %d %d %d %d", 0, 0, w, 0, w, h, 0, h);
+			m_pClipper = new CClipper(str, size, 1, 1);
+			if(!m_pClipper) return;
+		}
+
+		int da = (64<<8)/height;
+		int a = 0;
+		int k = m_effects[EF_SCROLL]->param[0]>>3;
+		int l = k+height;
+		if(k < 0) {a += -k*da; k = 0;}
+		if(l > h) {l = h;}
+
+		if(k < h)
+		{
+			BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
+
+			memset(m_pClipper->m_pAlphaMask, 0, am - m_pClipper->m_pAlphaMask);
+
+			for(int j = k; j < l; j++, a += da)
+			{
+				for(int i = 0; i < w; i++, am++)
+					*am = ((*am)*a)>>14;
+			}
+		}
+
+		da = -(64<<8)/height;
+		a = 0x40<<8;
+		l = m_effects[EF_SCROLL]->param[1]>>3;
+		k = l-height;
+		if(k < 0) {a += -k*da; k = 0;}
+		if(l > h) {l = h;}
+
+		if(k < h)
+		{
+			BYTE* am = &m_pClipper->m_pAlphaMask[k*w];
+
+			int j = k;
+			for(; j < l; j++, a += da)
+			{
+				for(int i = 0; i < w; i++, am++)
+					*am = ((*am)*a)>>14;
+			}
+
+			memset(am, 0, (h-j)*w);
+		}
+	}
+}
+
+void CSubtitle::MakeLines(CSize size, CRect marginRect)
+{
+	CSize spaceNeeded(0, 0);
+
+	bool fFirstLine = true;
+
+	m_topborder = m_bottomborder = 0;
+
+	CLine* l = NULL;
+	
+	POSITION pos = m_words.GetHeadPosition();
+	while(pos)
+	{
+		l = GetNextLine(pos, size.cx - marginRect.left - marginRect.right);
+		if(!l) break;
+
+		if(fFirstLine) {m_topborder = l->m_border; fFirstLine = false;}
+
+		spaceNeeded.cx = max(l->m_width, spaceNeeded.cx);
+		spaceNeeded.cy += l->m_ascent + l->m_descent;
+
+		AddTail(l);
+	}
+
+	if(l) m_bottomborder = l->m_border;
+
+	m_rect = CRect(
+		CPoint((m_scrAlignment%3) == 1 ? marginRect.left
+				: (m_scrAlignment%3) == 2 ? (marginRect.left + (size.cx - marginRect.right) - spaceNeeded.cx + 1) / 2
+				: (size.cx - marginRect.right - spaceNeeded.cx),
+				m_scrAlignment <= 3 ? (size.cy - marginRect.bottom - spaceNeeded.cy)
+				: m_scrAlignment <= 6 ? (marginRect.top + (size.cy - marginRect.bottom) - spaceNeeded.cy + 1) / 2
+				: marginRect.top),
+		spaceNeeded);
+}
+
+// CScreenLayoutAllocator
+
+void CScreenLayoutAllocator::Empty()
+{
+	m_subrects.RemoveAll();
+}
+
+void CScreenLayoutAllocator::AdvanceToSegment(int segment, const CAtlArray<int>& sa)
+{
+	POSITION pos = m_subrects.GetHeadPosition();
+	while(pos)
+	{
+		POSITION prev = pos;
+
+		SubRect& sr = m_subrects.GetNext(pos);
+
+		bool fFound = false;
+
+		if(abs(sr.segment - segment) <= 1) // using abs() makes it possible to play the subs backwards, too :)
+		{
+			for(int i = 0; i < sa.GetCount() && !fFound; i++)
+			{
+				if(sa[i] == sr.entry) 
+				{
+					sr.segment = segment;
+					fFound = true;
+				}
+			}
+		}
+
+		if(!fFound) m_subrects.RemoveAt(prev);
+	}
+}
+
+CRect CScreenLayoutAllocator::AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions)
+{
+	// TODO: handle collisions == 1 (reversed collisions)
+
+	POSITION pos = m_subrects.GetHeadPosition();
+	while(pos)
+	{
+		SubRect& sr = m_subrects.GetNext(pos);
+		if(sr.segment == segment && sr.entry == entry) 
+		{
+			return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
+		}
+	}
+
+	CRect r = s->m_rect + CRect(0, s->m_topborder, 0, s->m_bottomborder);
+
+	bool fSearchDown = s->m_scrAlignment > 3;
+
+	bool fOK;
+
+	do
+	{
+		fOK = true;
+
+		pos = m_subrects.GetHeadPosition();
+		while(pos)
+		{
+			SubRect& sr = m_subrects.GetNext(pos);
+
+			if(layer == sr.layer && !(r & sr.r).IsRectEmpty())
+			{
+				if(fSearchDown)
+				{
+					r.bottom = sr.r.bottom + r.Height();
+					r.top = sr.r.bottom;
+				}
+				else
+				{
+					r.top = sr.r.top - r.Height();
+					r.bottom = sr.r.top;
+				}
+				
+				fOK = false;
+			}
+		}
+	}
+	while(!fOK);
+
+	SubRect sr;
+	sr.r = r;
+	sr.segment = segment;
+	sr.entry = entry;
+	sr.layer = layer;
+	m_subrects.AddTail(sr);
+	
+	return(sr.r + CRect(0, -s->m_topborder, 0, -s->m_bottomborder));
+}
+
+// CRenderedTextSubtitle
+
+CRenderedTextSubtitle::CRenderedTextSubtitle(CCritSec* pLock)
+	: ISubPicProviderImpl(pLock)
+{
+	m_size = CSize(0, 0);
+
+	if(g_hDC_refcnt == 0) 
+	{
+		g_hDC = CreateCompatibleDC(NULL);
+		SetBkMode(g_hDC, TRANSPARENT); 
+		SetTextColor(g_hDC, 0xffffff); 
+		SetMapMode(g_hDC, MM_TEXT);
+	}
+
+	g_hDC_refcnt++;
+}
+
+CRenderedTextSubtitle::~CRenderedTextSubtitle()
+{
+	Deinit();
+
+	g_hDC_refcnt--;
+	if(g_hDC_refcnt == 0) DeleteDC(g_hDC);
+}
+
+void CRenderedTextSubtitle::Copy(CSimpleTextSubtitle& sts)
+{
+	__super::Copy(sts);
+
+	m_size = CSize(0, 0);
+
+	if(CRenderedTextSubtitle* pRTS = dynamic_cast<CRenderedTextSubtitle*>(&sts))
+	{
+		m_size = pRTS->m_size;
+	}
+}
+
+void CRenderedTextSubtitle::Empty()
+{
+	Deinit();
+
+	__super::Empty();
+}
+
+void CRenderedTextSubtitle::OnChanged()
+{
+	__super::OnChanged();
+
+	POSITION pos = m_subtitleCache.GetStartPosition();
+	while(pos)
+	{
+		int i;
+		CSubtitle* s;
+        m_subtitleCache.GetNextAssoc(pos, i, s);
+		delete s;
+	}
+
+	m_subtitleCache.RemoveAll();
+
+	m_sla.Empty();
+}
+
+bool CRenderedTextSubtitle::Init(CSize size, CRect vidrect)
+{
+	Deinit();
+
+	m_size = CSize(size.cx*8, size.cy*8);
+	m_vidrect = CRect(vidrect.left*8, vidrect.top*8, vidrect.right*8, vidrect.bottom*8);
+
+	m_sla.Empty();
+
+	return(true);
+}
+
+void CRenderedTextSubtitle::Deinit()
+{
+	POSITION pos = m_subtitleCache.GetStartPosition();
+	while(pos)
+	{
+		int i;
+		CSubtitle* s;
+        m_subtitleCache.GetNextAssoc(pos, i, s);
+		delete s;
+	}
+
+	m_subtitleCache.RemoveAll();
+
+	m_sla.Empty();
+
+	m_size = CSize(0, 0);
+	m_vidrect.SetRectEmpty();
+}
+
+void CRenderedTextSubtitle::ParseEffect(CSubtitle* sub, CString str)
+{
+	str.Trim();
+	if(!sub || str.IsEmpty()) return;
+
+	const TCHAR* s = _tcschr(str, ';');
+	if(!s) {s = (LPTSTR)(LPCTSTR)str; s += str.GetLength()-1;}
+	s++;
+	CString effect = str.Left(s - str);
+
+	if(!effect.CompareNoCase(_T("Banner;")))
+	{
+		int delay, lefttoright = 0, fadeawaywidth = 0;
+		if(_stscanf(s, _T("%d;%d;%d"), &delay, &lefttoright, &fadeawaywidth) < 1) return;
+
+		Effect* e = new Effect;
+		if(!e) return;
+
+		sub->m_effects[e->type = EF_BANNER] = e;
+		e->param[0] = (int)(max(1.0*delay/sub->m_scalex, 1));
+		e->param[1] = lefttoright;
+		e->param[2] = (int)(sub->m_scalex*fadeawaywidth);
+
+		sub->m_wrapStyle = 2;
+	}
+	else if(!effect.CompareNoCase(_T("Scroll up;")) || !effect.CompareNoCase(_T("Scroll down;")))
+	{
+		int top, bottom, delay, fadeawayheight = 0;
+		if(_stscanf(s, _T("%d;%d;%d;%d"), &top, &bottom, &delay, &fadeawayheight) < 3) return;
+
+		if(top > bottom) {int tmp = top; top = bottom; bottom = tmp;}
+
+		Effect* e = new Effect;
+		if(!e) return;
+
+		sub->m_effects[e->type = EF_SCROLL] = e;
+		e->param[0] = (int)(sub->m_scaley*top*8);
+		e->param[1] = (int)(sub->m_scaley*bottom*8);
+		e->param[2] = (int)(max(1.0*delay/sub->m_scaley, 1));
+		e->param[3] = (effect.GetLength() == 12);
+		e->param[4] = (int)(sub->m_scaley*fadeawayheight);
+	}
+}
+
+void CRenderedTextSubtitle::ParseString(CSubtitle* sub, CStringW str, STSStyle& style)
+{
+	if(!sub) return;
+
+	str.Replace(L"\\N", L"\n");
+	str.Replace(L"\\n", (sub->m_wrapStyle < 2 || sub->m_wrapStyle == 3) ? L" " : L"\n");
+	str.Replace(L"\\h", L"\x00A0");
+
+	for(int i = 0, j = 0, len = str.GetLength(); j <= len; j++)
+	{
+		WCHAR c = str[j];
+
+		if(c != '\n' && c != ' ' && c != '\x00A0' && c != 0)
+			continue;
+
+		if(i < j)
+		{
+			if(CWord* w = new CText(style, str.Mid(i, j-i), m_ktype, m_kstart, m_kend))
+			{
+				sub->m_words.AddTail(w); 
+				m_kstart = m_kend;
+			}
+		}
+
+		if(c == '\n')
+		{
+			if(CWord* w = new CText(style, CStringW(), m_ktype, m_kstart, m_kend))
+			{
+				sub->m_words.AddTail(w); 
+				m_kstart = m_kend;
+			}
+		}
+		else if(c == ' ' || c == '\x00A0')
+		{
+			if(CWord* w = new CText(style, CStringW(c), m_ktype, m_kstart, m_kend))
+			{
+				sub->m_words.AddTail(w); 
+				m_kstart = m_kend;
+			}
+		}
+
+		i = j+1;
+	}
+
+	return;
+}
+
+void CRenderedTextSubtitle::ParsePolygon(CSubtitle* sub, CStringW str, STSStyle& style)
+{
+	if(!sub || !str.GetLength() || !m_nPolygon) return;
+
+	if(CWord* w = new CPolygon(style, str, m_ktype, m_kstart, m_kend, sub->m_scalex/(1<<(m_nPolygon-1)), sub->m_scaley/(1<<(m_nPolygon-1)), m_polygonBaselineOffset))
+	{
+		sub->m_words.AddTail(w); 
+		m_kstart = m_kend;
+	}
+}
+
+bool CRenderedTextSubtitle::ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org, bool fAnimate)
+{
+	if(!sub) return(false);
+
+	int nTags = 0, nUnrecognizedTags = 0;
+
+	for(int i = 0, j; (j = str.Find('\\', i)) >= 0; i = j)
+	{
+		CStringW cmd;
+		for(WCHAR c = str[++j]; c && c != '(' && c != '\\'; cmd += c, c = str[++j]);
+		cmd.Trim();
+		if(cmd.IsEmpty()) continue;
+
+		CAtlArray<CStringW> params;
+
+		if(str[j] == '(')
+		{
+			CStringW param;
+			for(WCHAR c = str[++j]; c && c != ')'; param += c, c = str[++j]);
+			param.Trim();
+
+			while(!param.IsEmpty())
+			{
+				int i = param.Find(','), j = param.Find('\\');
+
+				if(i >= 0 && (j < 0 || i < j))
+				{
+					CStringW s = param.Left(i).Trim();
+					if(!s.IsEmpty()) params.Add(s);
+					param = i+1 < param.GetLength() ? param.Mid(i+1) : L"";
+				}
+				else
+				{
+					param.Trim();
+					if(!param.IsEmpty()) params.Add(param);
+					param.Empty();
+				}
+			}
+		}
+
+		if(!cmd.Find(L"1c") || !cmd.Find(L"2c") || !cmd.Find(L"3c") || !cmd.Find(L"4c"))
+			params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"1a") || !cmd.Find(L"2a") || !cmd.Find(L"3a") || !cmd.Find(L"4a"))
+			params.Add(cmd.Mid(2).Trim(L"&H")), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"alpha"))
+			params.Add(cmd.Mid(5).Trim(L"&H")), cmd = cmd.Left(5);
+		else if(!cmd.Find(L"an"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"a"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"bord"))
+			params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
+		else if(!cmd.Find(L"be"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"b"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"clip"))
+			;
+		else if(!cmd.Find(L"c"))
+			params.Add(cmd.Mid(1).Trim(L"&H")), cmd = cmd.Left(1);
+        else if(!cmd.Find(L"fade"))
+			;
+		else if(!cmd.Find(L"fe"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"fn"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"frx") || !cmd.Find(L"fry") || !cmd.Find(L"frz"))
+			params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
+		else if(!cmd.Find(L"fax") || !cmd.Find(L"fay"))
+			params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
+		else if(!cmd.Find(L"fr"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"fscx") || !cmd.Find(L"fscy"))
+			params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
+		else if(!cmd.Find(L"fsc"))
+			params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
+		else if(!cmd.Find(L"fsp"))
+			params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
+		else if(!cmd.Find(L"fs"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"i"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"kt") || !cmd.Find(L"kf") || !cmd.Find(L"ko"))
+			params.Add(cmd.Mid(2)), cmd = cmd.Left(2);
+		else if(!cmd.Find(L"k") || !cmd.Find(L"K"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"move"))
+			;
+		else if(!cmd.Find(L"org"))
+			;
+		else if(!cmd.Find(L"pbo"))
+			params.Add(cmd.Mid(3)), cmd = cmd.Left(3);
+		else if(!cmd.Find(L"pos"))
+			;
+		else if(!cmd.Find(L"p"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"q"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"r"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"shad"))
+			params.Add(cmd.Mid(4)), cmd = cmd.Left(4);
+		else if(!cmd.Find(L"s"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else if(!cmd.Find(L"t"))
+			;
+		else if(!cmd.Find(L"u"))
+			params.Add(cmd.Mid(1)), cmd = cmd.Left(1);
+		else
+			nUnrecognizedTags++;
+
+		nTags++;
+
+		// TODO: call ParseStyleModifier(cmd, params, ..) and move the rest there
+
+		CStringW p = params.GetCount() > 0 ? params[0] : L"";
+
+		if(cmd == "1c" || cmd == L"2c" || cmd == L"3c" || cmd == L"4c")
+		{
+			int i = cmd[0] - '1';
+
+			DWORD c = wcstol(p, NULL, 16);
+			style.colors[i] = !p.IsEmpty()
+				? (((int)CalcAnimation(c&0xff, style.colors[i]&0xff, fAnimate))&0xff
+				  |((int)CalcAnimation(c&0xff00, style.colors[i]&0xff00, fAnimate))&0xff00
+				  |((int)CalcAnimation(c&0xff0000, style.colors[i]&0xff0000, fAnimate))&0xff0000)
+				: org.colors[i];
+		}
+		else if(cmd == L"1a" || cmd == L"2a" || cmd == L"3a" || cmd == L"4a")
+		{
+			int i = cmd[0] - '1';
+
+			style.alpha[i] = !p.IsEmpty()
+				? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
+				: org.alpha[i];
+		}
+		else if(cmd == L"alpha")
+		{
+			for(int i = 0; i < 4; i++)
+			{
+				style.alpha[i] = !p.IsEmpty()
+					? (BYTE)CalcAnimation(wcstol(p, NULL, 16), style.alpha[i], fAnimate)
+					: org.alpha[i];
+			}
+		}
+		else if(cmd == L"an")
+		{
+			int n = wcstol(p, NULL, 10);
+			if(sub->m_scrAlignment < 0)
+                sub->m_scrAlignment = (n > 0 && n < 10) ? n : org.scrAlignment;
+		}
+		else if(cmd == L"a")
+		{
+			int n = wcstol(p, NULL, 10);
+			if(sub->m_scrAlignment < 0)
+                sub->m_scrAlignment = (n > 0 && n < 12) ? ((((n-1)&3)+1)+((n&4)?6:0)+((n&8)?3:0)) : org.scrAlignment;
+		}
+		else if(cmd == L"bord")
+		{
+			double n = CalcAnimation(wcstod(p, NULL), style.outlineWidth, fAnimate);
+			style.outlineWidth = !p.IsEmpty()
+				? (n < 0 ? 0 : n)
+				: org.outlineWidth;
+		}
+		else if(cmd == L"be")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.fBlur = !p.IsEmpty()
+				? (n == 0 ? false : n == 1 ? true : org.fBlur)
+				: org.fBlur;
+		}
+		else if(cmd == L"b")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.fontWeight = !p.IsEmpty()
+				? (n == 0 ? FW_NORMAL : n == 1 ? FW_BOLD : n >= 100 ? n : org.fontWeight)
+				: org.fontWeight;
+		}
+		else if(cmd == L"clip")
+		{
+			if(params.GetCount() == 1 && !sub->m_pClipper)
+			{
+				sub->m_pClipper = new CClipper(params[0], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex, sub->m_scaley);
+			}
+			else if(params.GetCount() == 2 && !sub->m_pClipper)
+			{
+				int scale = max(wcstol(p, NULL, 10), 1);
+				sub->m_pClipper = new CClipper(params[1], CSize(m_size.cx>>3, m_size.cy>>3), sub->m_scalex/(1<<(scale-1)), sub->m_scaley/(1<<(scale-1)));
+			}
+			else if(params.GetCount() == 4)
+			{
+				CRect r;
+
+				r.SetRect(
+					wcstol(params[0], NULL, 10),
+					wcstol(params[1], NULL, 10),
+					wcstol(params[2], NULL, 10),
+					wcstol(params[3], NULL, 10));
+
+				CPoint o(0, 0);
+
+				if(sub->m_relativeTo == 1) // TODO: this should also apply to the other two clippings above
+				{
+					o.x = m_vidrect.left>>3;
+					o.y = m_vidrect.top>>3;
+				}
+
+				sub->m_clip.SetRect(
+					(int)CalcAnimation(sub->m_scalex*r.left + o.x, sub->m_clip.left, fAnimate),
+					(int)CalcAnimation(sub->m_scaley*r.top + o.y, sub->m_clip.top, fAnimate),
+					(int)CalcAnimation(sub->m_scalex*r.right + o.x, sub->m_clip.right, fAnimate),
+					(int)CalcAnimation(sub->m_scaley*r.bottom + o.y, sub->m_clip.bottom, fAnimate));
+			}
+		}
+		else if(cmd == L"c")
+		{
+			DWORD c = wcstol(p, NULL, 16);
+			style.colors[0] = !p.IsEmpty()
+				? (((int)CalcAnimation(c&0xff, style.colors[0]&0xff, fAnimate))&0xff
+				  |((int)CalcAnimation(c&0xff00, style.colors[0]&0xff00, fAnimate))&0xff00
+				  |((int)CalcAnimation(c&0xff0000, style.colors[0]&0xff0000, fAnimate))&0xff0000)
+				: org.colors[0];
+		}
+        else if(cmd == L"fade" || cmd == L"fad")
+		{
+			if(params.GetCount() == 7 && !sub->m_effects[EF_FADE])// {\fade(a1=param[0], a2=param[1], a3=param[2], t1=t[0], t2=t[1], t3=t[2], t4=t[3])
+			{
+				if(Effect* e = new Effect)
+				{
+					for(int i = 0; i < 3; i++)
+						e->param[i] = wcstol(params[i], NULL, 10);
+					for(int i = 0; i < 4; i++)
+						e->t[i] = wcstol(params[3+i], NULL, 10);
+	                
+					sub->m_effects[EF_FADE] = e;
+				}
+			}
+			else if(params.GetCount() == 2 && !sub->m_effects[EF_FADE]) // {\fad(t1=t[1], t2=t[2])
+			{
+				if(Effect* e = new Effect)
+				{
+					e->param[0] = e->param[2] = 0xff;
+					e->param[1] = 0x00;
+					for(int i = 1; i < 3; i++) 
+						e->t[i] = wcstol(params[i-1], NULL, 10);
+					e->t[0] = e->t[3] = -1; // will be substituted with "start" and "end"
+
+					sub->m_effects[EF_FADE] = e;
+				}
+			}
+		}
+		else if(cmd == L"fax")
+		{
+			style.fontShiftX = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontShiftX, fAnimate)
+				: org.fontShiftX;
+		}
+		else if(cmd == L"fay")
+		{
+			style.fontShiftY = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontShiftY, fAnimate)
+				: org.fontShiftY;
+		}
+		else if(cmd == L"fe")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.charSet = !p.IsEmpty()
+				? n
+				: org.charSet;
+		}
+		else if(cmd == L"fn")
+		{
+			style.fontName = (!p.IsEmpty() && p != '0')
+				? CString(p).Trim()
+				: org.fontName;
+		}
+		else if(cmd == L"frx")
+		{
+			style.fontAngleX = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontAngleX, fAnimate)
+				: org.fontAngleX;
+		}
+		else if(cmd == L"fry")
+		{
+			style.fontAngleY = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontAngleY, fAnimate)
+				: org.fontAngleY;
+		}
+		else if(cmd == L"frz" || cmd == L"fr")
+		{
+			style.fontAngleZ = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontAngleZ, fAnimate)
+				: org.fontAngleZ;
+		}
+		else if(cmd == L"fscx")
+		{
+			double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleX, fAnimate);
+			style.fontScaleX = !p.IsEmpty()
+				? ((n < 0) ? 0 : n)
+				: org.fontScaleX;
+		}
+		else if(cmd == L"fscy")
+		{
+			double n = CalcAnimation(wcstol(p, NULL, 10), style.fontScaleY, fAnimate);
+			style.fontScaleY = !p.IsEmpty()
+				? ((n < 0) ? 0 : n)
+				: org.fontScaleY;
+		}
+		else if(cmd == L"fsc")
+		{
+			style.fontScaleX = org.fontScaleX;
+			style.fontScaleY = org.fontScaleY;
+		}
+		else if(cmd == L"fsp")
+		{
+			style.fontSpacing = !p.IsEmpty()
+				? CalcAnimation(wcstod(p, NULL), style.fontSpacing, fAnimate)
+				: org.fontSpacing;
+		}
+		else if(cmd == L"fs")
+		{
+			if(!p.IsEmpty())
+			{
+				if(p[0] == '-' || p[0] == '+')
+				{
+					double n = CalcAnimation(style.fontSize + style.fontSize*wcstol(p, NULL, 10)/10, style.fontSize, fAnimate);
+					style.fontSize = (n > 0) ? n : org.fontSize;
+				}
+				else
+				{
+					double n = CalcAnimation(wcstol(p, NULL, 10), style.fontSize, fAnimate);
+					style.fontSize = (n > 0) ? n : org.fontSize;
+				}
+			}
+			else
+			{
+				style.fontSize = org.fontSize;
+			}
+		}
+		else if(cmd == L"i")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.fItalic = !p.IsEmpty()
+				? (n == 0 ? false : n == 1 ? true : org.fItalic)
+				: org.fItalic;
+		}
+		else if(cmd == L"kt")
+		{
+			m_kstart = !p.IsEmpty() 
+				? wcstol(p, NULL, 10)*10
+				: 0;
+			m_kend = m_kstart;
+		}
+		else if(cmd == L"kf" || cmd == L"K")
+		{
+			m_ktype = 1;
+			m_kstart = m_kend;
+			m_kend += !p.IsEmpty() 
+				? wcstol(p, NULL, 10)*10
+				: 1000;
+		}
+		else if(cmd == L"ko")
+		{
+			m_ktype = 2;
+			m_kstart = m_kend;
+			m_kend += !p.IsEmpty() 
+				? wcstol(p, NULL, 10)*10
+				: 1000;
+		}
+		else if(cmd == L"k")
+		{
+			m_ktype = 0;
+			m_kstart = m_kend;
+			m_kend += !p.IsEmpty() 
+				? wcstol(p, NULL, 10)*10
+				: 1000;
+		}
+		else if(cmd == L"move") // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3][, t1=t[0], t2=t[1]])}
+		{
+			if((params.GetCount() == 4 || params.GetCount() == 6) && !sub->m_effects[EF_MOVE])
+			{
+				if(Effect* e = new Effect)
+				{
+					e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
+					e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
+					e->param[2] = (int)(sub->m_scalex*wcstod(params[2], NULL)*8);
+					e->param[3] = (int)(sub->m_scaley*wcstod(params[3], NULL)*8);
+
+					e->t[0] = e->t[1] = -1;
+
+					if(params.GetCount() == 6)
+					{
+						for(int i = 0; i < 2; i++)
+							e->t[i] = wcstol(params[4+i], NULL, 10);
+					}
+
+					sub->m_effects[EF_MOVE] = e;
+				}
+			}
+		}
+		else if(cmd == L"org") // {\org(x=param[0], y=param[1])}
+		{
+			if(params.GetCount() == 2 && !sub->m_effects[EF_ORG])
+			{
+				if(Effect* e = new Effect)
+				{
+					e->param[0] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
+					e->param[1] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
+
+					sub->m_effects[EF_ORG] = e;
+				}
+			}
+		}
+		else if(cmd == L"pbo")
+		{
+			m_polygonBaselineOffset = wcstol(p, NULL, 10);
+		}
+		else if(cmd == L"pos")
+		{
+			if(params.GetCount() == 2 && !sub->m_effects[EF_MOVE])
+			{
+				if(Effect* e = new Effect)
+				{
+					e->param[0] = e->param[2] = (int)(sub->m_scalex*wcstod(params[0], NULL)*8);
+					e->param[1] = e->param[3] = (int)(sub->m_scaley*wcstod(params[1], NULL)*8);
+					e->t[0] = e->t[1] = 0;
+
+					sub->m_effects[EF_MOVE] = e;
+				}
+			}
+		}
+		else if(cmd == L"p")
+		{
+			int n = wcstol(p, NULL, 10);
+			m_nPolygon = (n <= 0 ? 0 : n);
+		}
+		else if(cmd == L"q")
+		{
+			int n = wcstol(p, NULL, 10);
+			sub->m_wrapStyle = !p.IsEmpty() && (0 <= n && n <= 3)
+				? n
+				: m_defaultWrapStyle;
+		}
+		else if(cmd == L"r")
+		{
+			STSStyle* val;
+			style = (!p.IsEmpty() && m_styles.Lookup(CString(p), val) && val) ? *val : org;
+		}
+		else if(cmd == L"shad")
+		{
+			double n = CalcAnimation(wcstod(p, NULL), style.shadowDepth, fAnimate);
+			style.shadowDepth = !p.IsEmpty()
+				? (n < 0 ? 0 : n)
+				: org.shadowDepth;
+		}
+		else if(cmd == L"s")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.fStrikeOut = !p.IsEmpty()
+				? (n == 0 ? false : n == 1 ? true : org.fStrikeOut)
+				: org.fStrikeOut;
+		}
+		else if(cmd == L"t") // \t([<t1>,<t2>,][<accel>,]<style modifiers>)
+		{
+			p.Empty();
+
+			m_animStart = m_animEnd = 0;
+			m_animAccel = 1;
+
+			if(params.GetCount() == 1)
+			{
+				p = params[0];
+			}
+			else if(params.GetCount() == 2)
+			{
+				m_animAccel = wcstod(params[0], NULL);
+				p = params[1];
+			}
+			else if(params.GetCount() == 3)
+			{
+				m_animStart = (int)wcstod(params[0], NULL); 
+				m_animEnd = (int)wcstod(params[1], NULL);
+				p = params[2];
+			}
+			else if(params.GetCount() == 4)
+			{
+				m_animStart = wcstol(params[0], NULL, 10); 
+				m_animEnd = wcstol(params[1], NULL, 10);
+				m_animAccel = wcstod(params[2], NULL);
+				p = params[3];
+			}
+
+			ParseSSATag(sub, p, style, org, true);
+
+			sub->m_fAnimated = true;
+		}
+		else if(cmd == L"u")
+		{
+			int n = wcstol(p, NULL, 10);
+			style.fUnderline = !p.IsEmpty()
+				? (n == 0 ? false : n == 1 ? true : org.fUnderline)
+				: org.fUnderline;
+		}
+	}
+
+//	return(nUnrecognizedTags < nTags);
+	return(true); // there are ppl keeping coments inside {}, lets make them happy now
+}
+
+bool CRenderedTextSubtitle::ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org)
+{
+	if(str.Find(L"!--") == 0)
+		return(true);
+
+	bool fClosing = str[0] == '/';
+	str.Trim(L" /");
+
+	int i = str.Find(' ');
+	if(i < 0) i = str.GetLength();
+
+	CStringW tag = str.Left(i).MakeLower();
+	str = str.Mid(i).Trim();
+
+	CAtlArray<CStringW> attribs, params;
+	while((i = str.Find('=')) > 0)
+	{
+		attribs.Add(str.Left(i).Trim().MakeLower());
+		str = str.Mid(i+1);
+		for(i = 0; _istspace(str[i]); i++);
+		str = str.Mid(i);
+		if(str[0] == '\"') {str = str.Mid(1); i = str.Find('\"');}
+		else i = str.Find(' ');
+		if(i < 0) i = str.GetLength();
+		params.Add(str.Left(i).Trim().MakeLower());
+		str = str.Mid(i+1);
+	}
+
+	if(tag == L"text")
+		;
+	else if(tag == L"b" || tag == L"strong")
+		style.fontWeight = !fClosing ? FW_BOLD : org.fontWeight;
+	else if(tag == L"i" || tag == L"em")
+		style.fItalic = !fClosing ? true : org.fItalic;
+	else if(tag == L"u")
+		style.fUnderline = !fClosing ? true : org.fUnderline;
+	else if(tag == L"s" || tag == L"strike" || tag == L"del")
+		style.fStrikeOut = !fClosing ? true : org.fStrikeOut;
+	else if(tag == L"font")
+	{
+		if(!fClosing)
+		{
+			for(i = 0; i < attribs.GetCount(); i++)
+			{
+				if(params[i].IsEmpty()) continue;
+
+				int nColor = -1;
+
+				if(attribs[i] == L"face")
+				{
+					style.fontName = params[i];
+				}
+				else if(attribs[i] == L"size")
+				{
+					if(params[i][0] == '+')
+						style.fontSize += wcstol(params[i], NULL, 10);
+					else if(params[i][0] == '-')
+						style.fontSize -= wcstol(params[i], NULL, 10);
+					else
+						style.fontSize = wcstol(params[i], NULL, 10);
+				}
+				else if(attribs[i] == L"color")
+				{
+					nColor = 0;
+				}
+				else if(attribs[i] == L"outline-color")
+				{
+					nColor = 2;
+				}
+				else if(attribs[i] == L"outline-level")
+				{
+					style.outlineWidth = wcstol(params[i], NULL, 10);
+				}
+				else if(attribs[i] == L"shadow-color")
+				{
+					nColor = 3;
+				}
+				else if(attribs[i] == L"shadow-level")
+				{
+					style.shadowDepth = wcstol(params[i], NULL, 10);
+				}
+
+				if(nColor >= 0 && nColor < 4)
+				{
+					CString key = WToT(params[i]).TrimLeft('#');
+					DWORD val;
+					if(g_colors.Lookup(key, val))
+						style.colors[nColor] = val;
+					else if((style.colors[nColor] = _tcstol(key, NULL, 16)) == 0)
+						style.colors[nColor] = 0x00ffffff;  // default is white
+					style.colors[nColor] = ((style.colors[nColor]>>16)&0xff)|((style.colors[nColor]&0xff)<<16)|(style.colors[nColor]&0x00ff00);
+				}
+			}
+		}
+		else
+		{
+			style.fontName = org.fontName;
+			style.fontSize = org.fontSize;
+			memcpy(style.colors, org.colors, sizeof(style.colors));
+		}
+	}
+	else if(tag == L"k" && attribs.GetCount() == 1 && attribs[0] == L"t")
+	{
+		m_ktype = 1;
+		m_kstart = m_kend;
+		m_kend += wcstol(params[0], NULL, 10);
+	}
+	else 
+		return(false);
+
+	return(true);
+}
+
+double CRenderedTextSubtitle::CalcAnimation(double dst, double src, bool fAnimate)
+{
+	int s = m_animStart ? m_animStart : 0;
+	int e = m_animEnd ? m_animEnd : m_delay;
+
+	if(fabs(dst-src) >= 0.0001 && fAnimate)
+	{
+		if(m_time < s) dst = src;
+		else if(s <= m_time && m_time < e)
+		{
+			double t = pow(1.0 * (m_time - s) / (e - s), m_animAccel);
+			dst = (1 - t) * src + t * dst;
+		}
+//		else dst = dst;
+	}
+
+	return(dst);
+}
+
+CSubtitle* CRenderedTextSubtitle::GetSubtitle(int entry)
+{
+	CSubtitle* sub;
+	if(m_subtitleCache.Lookup(entry, sub)) 
+	{
+		if(sub->m_fAnimated) {delete sub; sub = NULL;}
+		else return(sub);
+	}
+
+	sub = new CSubtitle();
+	if(!sub) return(NULL);
+
+	CStringW str = GetStrW(entry, true);
+
+	STSStyle stss, orgstss;
+	GetStyle(entry, stss);
+	orgstss = stss;
+
+	sub->m_clip.SetRect(0, 0, m_size.cx>>3, m_size.cy>>3);
+	sub->m_scrAlignment = -stss.scrAlignment;
+	sub->m_wrapStyle = m_defaultWrapStyle;
+	sub->m_fAnimated = false;
+	sub->m_relativeTo = stss.relativeTo;
+
+	sub->m_scalex = m_dstScreenSize.cx > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Width() : m_size.cx) / (m_dstScreenSize.cx*8) : 1.0;
+	sub->m_scaley = m_dstScreenSize.cy > 0 ? 1.0 * (stss.relativeTo == 1 ? m_vidrect.Height() : m_size.cy) / (m_dstScreenSize.cy*8) : 1.0;
+
+	m_animStart = m_animEnd = 0;
+	m_animAccel = 1;
+	m_ktype = m_kstart = m_kend = 0;
+	m_nPolygon = 0;
+	m_polygonBaselineOffset = 0;
+
+	ParseEffect(sub, GetAt(entry).effect);
+
+	while(!str.IsEmpty())
+	{
+		bool fParsed = false;
+
+		int i;
+
+		if(str[0] == '{' && (i = str.Find(L'}')) > 0)
+		{
+			if(fParsed = ParseSSATag(sub, str.Mid(1, i-1), stss, orgstss))
+				str = str.Mid(i+1);
+		}
+		else if(str[0] == '<' && (i = str.Find(L'>')) > 0)
+		{
+			if(fParsed = ParseHtmlTag(sub, str.Mid(1, i-1), stss, orgstss))
+				str = str.Mid(i+1);
+		}
+
+		if(fParsed)
+		{
+			i = str.FindOneOf(L"{<");
+			if(i < 0) i = str.GetLength();
+			if(i == 0) continue;
+		}
+		else
+		{
+			i = str.Mid(1).FindOneOf(L"{<");
+			if(i < 0) i = str.GetLength()-1;
+			i++;
+		}
+
+		STSStyle tmp = stss;
+
+		tmp.fontSize = sub->m_scaley*tmp.fontSize*64;
+		tmp.fontSpacing = sub->m_scalex*tmp.fontSpacing*64;
+		tmp.outlineWidth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
+		tmp.shadowDepth *= (m_fScaledBAS ? ((sub->m_scalex+sub->m_scaley)/2) : 1) * 8;
+
+		if(m_nPolygon)
+		{
+			ParsePolygon(sub, str.Left(i), tmp);
+		}
+		else
+		{
+			ParseString(sub, str.Left(i), tmp);
+		}
+
+		str = str.Mid(i);
+	}
+
+	// just a "work-around" solution... in most cases nobody will want to use \org together with moving but without rotating the subs
+	if(sub->m_effects[EF_ORG] && (sub->m_effects[EF_MOVE] || sub->m_effects[EF_BANNER] || sub->m_effects[EF_SCROLL]))
+		sub->m_fAnimated = true;
+
+	sub->m_scrAlignment = abs(sub->m_scrAlignment);
+
+	STSEntry stse = GetAt(entry);
+	CRect marginRect = stse.marginRect;
+	if(marginRect.left == 0) marginRect.left = orgstss.marginRect.left;
+	if(marginRect.top == 0) marginRect.top = orgstss.marginRect.top;
+	if(marginRect.right == 0) marginRect.right = orgstss.marginRect.right;
+	if(marginRect.bottom == 0) marginRect.bottom = orgstss.marginRect.bottom;
+	marginRect.left = (int)(sub->m_scalex*marginRect.left*8);
+	marginRect.top = (int)(sub->m_scaley*marginRect.top*8);
+	marginRect.right = (int)(sub->m_scalex*marginRect.right*8);
+	marginRect.bottom = (int)(sub->m_scaley*marginRect.bottom*8);
+	
+	if(stss.relativeTo == 1)
+	{
+		marginRect.left += m_vidrect.left;
+		marginRect.top += m_vidrect.top;
+		marginRect.right += m_size.cx - m_vidrect.right;
+		marginRect.bottom += m_size.cy - m_vidrect.bottom;
+	}
+
+	sub->CreateClippers(m_size);
+
+	sub->MakeLines(m_size, marginRect);
+
+	m_subtitleCache[entry] = sub;
+
+	return(sub);
+}
+
+//
+
+STDMETHODIMP CRenderedTextSubtitle::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+    CheckPointer(ppv, E_POINTER);
+    *ppv = NULL;
+
+    return 
+		QI(IPersist)
+		QI(ISubStream)
+		QI(ISubPicProvider)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicProvider
+
+STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetStartPosition(REFERENCE_TIME rt, double fps)
+{
+	int iSegment = -1;
+	SearchSubs((int)(rt/10000), fps, &iSegment, NULL);
+	
+	if(iSegment < 0) iSegment = 0;
+
+	return(GetNext((POSITION)iSegment));
+}
+
+STDMETHODIMP_(POSITION) CRenderedTextSubtitle::GetNext(POSITION pos)
+{
+	int iSegment = (int)pos;
+
+	const STSSegment* stss;
+	while((stss = GetSegment(iSegment)) && stss->subs.GetCount() == 0)
+		iSegment++;
+
+	return(stss ? (POSITION)(iSegment+1) : NULL);
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStart(POSITION pos, double fps)
+{
+	return(10000i64 * TranslateSegmentStart((int)pos-1, fps));
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CRenderedTextSubtitle::GetStop(POSITION pos, double fps)
+{
+	return(10000i64 * TranslateSegmentEnd((int)pos-1, fps));
+}
+
+STDMETHODIMP_(bool) CRenderedTextSubtitle::IsAnimated(POSITION pos)
+{
+	// TODO
+	return(true);
+}
+
+struct LSub {int idx, layer, readorder;};
+
+static int lscomp(const void* ls1, const void* ls2)
+{
+	int ret = ((LSub*)ls1)->layer - ((LSub*)ls2)->layer;
+	if(!ret) ret = ((LSub*)ls1)->readorder - ((LSub*)ls2)->readorder;
+	return(ret);
+}
+
+STDMETHODIMP CRenderedTextSubtitle::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
+{
+	CRect bbox2(0,0,0,0);
+
+	if(m_size != CSize(spd.w*8, spd.h*8) || m_vidrect != CRect(spd.vidrect.left*8, spd.vidrect.top*8, spd.vidrect.right*8, spd.vidrect.bottom*8))
+		Init(CSize(spd.w, spd.h), spd.vidrect);
+
+	int t = (int)(rt / 10000);
+
+	int segment;
+	const STSSegment* stss = SearchSubs(t, fps, &segment);
+	if(!stss) return S_FALSE;
+
+	// clear any cached subs not in the range of +/-30secs measured from the segment's bounds
+	{
+		POSITION pos = m_subtitleCache.GetStartPosition();
+		while(pos)
+		{
+			int key;
+			CSubtitle* value;
+			m_subtitleCache.GetNextAssoc(pos, key, value);
+
+			STSEntry& stse = GetAt(key);
+			if(stse.end <= (t-30000) || stse.start > (t+30000)) 
+			{
+				delete value;
+				m_subtitleCache.RemoveKey(key);
+				pos = m_subtitleCache.GetStartPosition();
+			}
+		}
+	}
+
+	m_sla.AdvanceToSegment(segment, stss->subs);
+
+	CAtlArray<LSub> subs;
+
+	for(int i = 0, j = stss->subs.GetCount(); i < j; i++)
+	{
+		LSub ls;
+		ls.idx = stss->subs[i];
+		ls.layer = GetAt(stss->subs[i]).layer;
+		ls.readorder = GetAt(stss->subs[i]).readorder;
+		subs.Add(ls);
+	}
+
+	qsort(subs.GetData(), subs.GetCount(), sizeof(LSub), lscomp);
+
+	for(int i = 0, j = subs.GetCount(); i < j; i++)
+	{
+		int entry = subs[i].idx;
+
+		STSEntry stse = GetAt(entry);
+
+		{
+			int start = TranslateStart(entry, fps);
+			m_time = t - start;
+			m_delay = TranslateEnd(entry, fps) - start;
+		}
+
+		CSubtitle* s = GetSubtitle(entry);
+		if(!s) continue;
+
+		CRect clipRect = s->m_clip;
+		CRect r = s->m_rect;
+		CSize spaceNeeded = r.Size();
+
+		// apply the effects
+
+		bool fPosOverride = false, fOrgOverride = false;
+
+		int alpha = 0x00;
+
+		CPoint org2;
+
+		for(int k = 0; k < EF_NUMBEROFEFFECTS; k++)
+		{
+			if(!s->m_effects[k]) continue;
+
+			switch(k)
+			{
+			case EF_MOVE: // {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])}
+				{
+					CPoint p;
+					CPoint p1(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
+					CPoint p2(s->m_effects[k]->param[2], s->m_effects[k]->param[3]);
+					int t1 = s->m_effects[k]->t[0];
+					int t2 = s->m_effects[k]->t[1];
+
+					if(t2 < t1) {int t = t1; t1 = t2; t2 = t;}
+
+					if(t1 <= 0 && t2 <= 0) {t1 = 0; t2 = m_delay;}
+
+					if(m_time <= t1) p = p1;
+					else if(t1 < m_time && m_time < t2)
+					{
+						double t = 1.0*(m_time-t1)/(t2-t1);
+						p.x = (int)((1-t)*p1.x + t*p2.x);
+						p.y = (int)((1-t)*p1.y + t*p2.y);
+					}
+					else p = p2;
+
+					r = CRect(
+							CPoint((s->m_scrAlignment%3) == 1 ? p.x : (s->m_scrAlignment%3) == 0 ? p.x - spaceNeeded.cx : p.x - (spaceNeeded.cx+1)/2,
+									s->m_scrAlignment <= 3 ? p.y - spaceNeeded.cy : s->m_scrAlignment <= 6 ? p.y - (spaceNeeded.cy+1)/2 : p.y),
+							spaceNeeded);
+
+					if(s->m_relativeTo == 1)
+						r.OffsetRect(m_vidrect.TopLeft());
+
+					fPosOverride = true;
+				}
+				break;
+			case EF_ORG: // {\org(x=param[0], y=param[1])}
+				{
+					org2 = CPoint(s->m_effects[k]->param[0], s->m_effects[k]->param[1]);
+
+					fOrgOverride = true;
+				}
+				break;
+			case EF_FADE: // {\fade(a1=param[0], a2=param[1], a3=param[2], t1=t[0], t2=t[1], t3=t[2], t4=t[3]) or {\fad(t1=t[1], t2=t[2])
+				{
+					int t1 = s->m_effects[k]->t[0];
+					int t2 = s->m_effects[k]->t[1];
+					int t3 = s->m_effects[k]->t[2];
+					int t4 = s->m_effects[k]->t[3];
+
+					if(t1 == -1 && t4 == -1) {t1 = 0; t3 = m_delay-t3; t4 = m_delay;}
+
+					if(m_time < t1) alpha = s->m_effects[k]->param[0];
+					else if(m_time >= t1 && m_time < t2)
+					{
+						double t = 1.0 * (m_time - t1) / (t2 - t1);
+						alpha = (int)(s->m_effects[k]->param[0]*(1-t) + s->m_effects[k]->param[1]*t);
+					}
+					else if(m_time >= t2 && m_time < t3) alpha = s->m_effects[k]->param[1];
+					else if(m_time >= t3 && m_time < t4)
+					{
+						double t = 1.0 * (m_time - t3) / (t4 - t3);
+						alpha = (int)(s->m_effects[k]->param[1]*(1-t) + s->m_effects[k]->param[2]*t);
+					}
+					else if(m_time >= t4) alpha = s->m_effects[k]->param[2];
+				}
+				break;
+			case EF_BANNER: // Banner;delay=param[0][;leftoright=param[1];fadeawaywidth=param[2]]
+				{
+					int left = s->m_relativeTo == 1 ? m_vidrect.left : 0, 
+						right = s->m_relativeTo == 1 ? m_vidrect.right : m_size.cx;
+
+					r.left = !!s->m_effects[k]->param[1] 
+						? (left/*marginRect.left*/ - spaceNeeded.cx) + (int)(m_time*8.0/s->m_effects[k]->param[0])
+						: (right /*- marginRect.right*/) - (int)(m_time*8.0/s->m_effects[k]->param[0]);
+
+					r.right = r.left + spaceNeeded.cx;
+
+					clipRect &= CRect(left>>3, clipRect.top, right>>3, clipRect.bottom);
+
+					fPosOverride = true;
+				}
+				break;
+			case EF_SCROLL: // Scroll up/down(toptobottom=param[3]);top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
+				{
+					r.top = !!s->m_effects[k]->param[3]
+						? s->m_effects[k]->param[0] + (int)(m_time*8.0/s->m_effects[k]->param[2]) - spaceNeeded.cy
+						: s->m_effects[k]->param[1] - (int)(m_time*8.0/s->m_effects[k]->param[2]);
+
+					r.bottom = r.top + spaceNeeded.cy;
+
+					CRect cr(0, (s->m_effects[k]->param[0] + 4) >> 3, spd.w, (s->m_effects[k]->param[1] + 4) >> 3);
+
+					if(s->m_relativeTo == 1)
+						r.top += m_vidrect.top, 
+						r.bottom += m_vidrect.top, 
+						cr.top += m_vidrect.top>>3, 
+						cr.bottom += m_vidrect.top>>3;
+
+					clipRect &= cr;
+
+					fPosOverride = true;
+				}
+				break;
+			default:
+				break;
+			}
+		}
+
+		if(!fPosOverride && !fOrgOverride && !s->m_fAnimated) 
+			r = m_sla.AllocRect(s, segment, entry, stse.layer, m_collisions);
+
+		CPoint org;
+		org.x = (s->m_scrAlignment%3) == 1 ? r.left : (s->m_scrAlignment%3) == 2 ? r.CenterPoint().x : r.right;
+		org.y = s->m_scrAlignment <= 3 ? r.bottom : s->m_scrAlignment <= 6 ? r.CenterPoint().y : r.top;
+
+		if(!fOrgOverride) org2 = org;
+
+		BYTE* pAlphaMask = s->m_pClipper?s->m_pClipper->m_pAlphaMask:NULL;
+
+		CPoint p, p2(0, r.top);
+
+		POSITION pos;
+
+		p = p2;
+
+		pos = s->GetHeadPosition();
+		while(pos) 
+		{
+			CLine* l = s->GetNext(pos);
+
+			p.x = (s->m_scrAlignment%3) == 1 ? org.x
+				: (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
+				:							   org.x - (l->m_width/2);
+
+			bbox2 |= l->PaintShadow(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
+
+			p.y += l->m_ascent + l->m_descent;
+		}
+
+		p = p2;
+
+		pos = s->GetHeadPosition();
+		while(pos) 
+		{
+			CLine* l = s->GetNext(pos);
+
+			p.x = (s->m_scrAlignment%3) == 1 ? org.x
+				: (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
+				:							   org.x - (l->m_width/2);
+
+			bbox2 |= l->PaintOutline(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
+
+			p.y += l->m_ascent + l->m_descent;
+		}
+
+		p = p2;
+
+		pos = s->GetHeadPosition();
+		while(pos) 
+		{
+			CLine* l = s->GetNext(pos);
+
+			p.x = (s->m_scrAlignment%3) == 1 ? org.x
+				: (s->m_scrAlignment%3) == 0 ? org.x - l->m_width
+				:							   org.x - (l->m_width/2);
+
+			bbox2 |= l->PaintBody(spd, clipRect, pAlphaMask, p, org2, m_time, alpha);
+
+			p.y += l->m_ascent + l->m_descent;
+		}
+	}
+
+	bbox = bbox2;
+
+	return (subs.GetCount() && !bbox2.IsRectEmpty()) ? S_OK : S_FALSE;
+}
+
+// IPersist
+
+STDMETHODIMP CRenderedTextSubtitle::GetClassID(CLSID* pClassID)
+{
+	return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
+}
+
+// ISubStream
+
+STDMETHODIMP_(int) CRenderedTextSubtitle::GetStreamCount()
+{
+	return(1);
+}
+
+STDMETHODIMP CRenderedTextSubtitle::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
+{
+	if(iStream != 0) return E_INVALIDARG;
+
+	if(ppName)
+	{
+		if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
+			return E_OUTOFMEMORY;
+
+		wcscpy(*ppName, CStringW(m_name));
+	}
+
+	if(pLCID)
+	{
+		*pLCID = 0; // TODO
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP_(int) CRenderedTextSubtitle::GetStream()
+{
+	return(0);
+}
+
+STDMETHODIMP CRenderedTextSubtitle::SetStream(int iStream)
+{
+	return iStream == 0 ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP CRenderedTextSubtitle::Reload()
+{
+	CFileStatus s;
+	if(!CFile::GetStatus(m_path, s)) return E_FAIL;
+	return !m_path.IsEmpty() && Open(m_path, DEFAULT_CHARSET) ? S_OK : E_FAIL;
+}
diff --git a/vsfilter/subtitles/RTS.h b/vsfilter/subtitles/RTS.h
new file mode 100644
index 0000000000000000000000000000000000000000..d63d3ce6d1f2a438b241d372be0ab5639b8a8e9e
--- /dev/null
+++ b/vsfilter/subtitles/RTS.h
@@ -0,0 +1,267 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "STS.h"
+#include "Rasterizer.h"
+#include "..\SubPic\ISubPic.h"
+
+class CMyFont : public CFont
+{
+public:
+	int m_ascent, m_descent;
+
+	CMyFont(STSStyle& style);
+};
+
+class CPolygon;
+
+class CWord : public Rasterizer
+{
+	bool m_fDrawn;
+	CPoint m_p;
+
+	void Transform(CPoint org);
+
+	bool CreateOpaqueBox();
+
+protected:
+	CStringW m_str;
+
+	virtual bool CreatePath() = 0;
+
+public:
+	bool m_fWhiteSpaceChar, m_fLineBreak;
+
+	STSStyle m_style;
+
+	CPolygon* m_pOpaqueBox;
+
+	int m_ktype, m_kstart, m_kend;
+
+	int m_width, m_ascent, m_descent;
+
+	CWord(STSStyle& style, CStringW str, int ktype, int kstart, int kend); // str[0] = 0 -> m_fLineBreak = true (in this case we only need and use the height of m_font from the whole class)
+	virtual ~CWord();
+
+	virtual CWord* Copy() = 0;
+	virtual bool Append(CWord* w);
+
+	void Paint(CPoint p, CPoint org);
+};
+
+class CText : public CWord
+{
+protected:
+	virtual bool CreatePath();
+
+public:
+	CText(STSStyle& style, CStringW str, int ktype, int kstart, int kend);
+
+	virtual CWord* Copy();
+	virtual bool Append(CWord* w);
+};
+
+class CPolygon : public CWord
+{
+	bool GetLONG(CStringW& str, LONG& ret);
+	bool GetPOINT(CStringW& str, POINT& ret);
+	bool ParseStr();
+
+protected:
+	double m_scalex, m_scaley;
+	int m_baseline;
+
+	CAtlArray<BYTE> m_pathTypesOrg;
+	CAtlArray<CPoint> m_pathPointsOrg;
+
+	virtual bool CreatePath();
+
+public:
+	CPolygon(STSStyle& style, CStringW str, int ktype, int kstart, int kend, double scalex, double scaley, int baseline);
+	virtual ~CPolygon();
+
+	virtual CWord* Copy();
+	virtual bool Append(CWord* w);
+};
+
+class CClipper : public CPolygon
+{
+private:
+	CWord* Copy();
+    virtual bool Append(CWord* w);
+
+public:
+	CClipper(CStringW str, CSize size, double scalex, double scaley);
+	virtual ~CClipper();
+
+	CSize m_size;
+	BYTE* m_pAlphaMask;
+};
+
+class CLine : public CAtlList<CWord*>
+{
+public:
+	int m_width, m_ascent, m_descent, m_border;
+
+	virtual ~CLine();
+
+	void Compact();
+
+	CRect PaintShadow(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha);
+	CRect PaintOutline(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha);
+	CRect PaintBody(SubPicDesc& spd, CRect& clipRect, BYTE* pAlphaMask, CPoint p, CPoint org, int time, int alpha);
+};
+
+enum eftype
+{
+	EF_MOVE = 0,	// {\move(x1=param[0], y1=param[1], x2=param[2], y2=param[3], t1=t[0], t2=t[1])} or {\pos(x=param[0], y=param[1])}
+	EF_ORG,			// {\org(x=param[0], y=param[1])}
+	EF_FADE,		// {\fade(a1=param[0], a2=param[1], a3=param[2], t1=t[0], t2=t[1], t3=t[2], t4=t[3])} or {\fad(t1=t[1], t2=t[2])
+	EF_BANNER,		// Banner;delay=param[0][;lefttoright=param[1];fadeawaywidth=param[2]]
+	EF_SCROLL,		// Scroll up/down=param[3];top=param[0];bottom=param[1];delay=param[2][;fadeawayheight=param[4]]
+};
+
+#define EF_NUMBEROFEFFECTS 5
+
+class Effect
+{
+public:
+	enum eftype type;
+	int param[8];
+	int t[4];
+};
+
+class CSubtitle : public CAtlList<CLine*>
+{
+	int GetFullWidth();
+	int GetFullLineWidth(POSITION pos);
+	int GetWrapWidth(POSITION pos, int maxwidth);
+	CLine* GetNextLine(POSITION& pos, int maxwidth);
+
+public:
+	int m_scrAlignment;
+	int m_wrapStyle;
+	bool m_fAnimated;
+	int m_relativeTo;
+
+	Effect* m_effects[EF_NUMBEROFEFFECTS];
+
+	CAtlList<CWord*> m_words;
+
+	CClipper* m_pClipper;
+
+	CRect m_rect, m_clip;
+	int m_topborder, m_bottomborder;
+
+	double m_scalex, m_scaley;
+
+public:
+	CSubtitle();
+	virtual ~CSubtitle();
+	virtual void Empty();
+
+	void CreateClippers(CSize size);
+
+	void MakeLines(CSize size, CRect marginRect);
+};
+
+class CScreenLayoutAllocator
+{
+	typedef struct
+	{
+		CRect r;
+		int segment, entry, layer;
+	} SubRect;
+
+	CAtlList<SubRect> m_subrects;
+
+public:
+	virtual void Empty();
+
+	void AdvanceToSegment(int segment, const CAtlArray<int>& sa);
+	CRect AllocRect(CSubtitle* s, int segment, int entry, int layer, int collisions);
+};
+
+[uuid("537DCACA-2812-4a4f-B2C6-1A34C17ADEB0")]
+class CRenderedTextSubtitle : public CSimpleTextSubtitle, public ISubPicProviderImpl, public ISubStream
+{
+	CAtlMap<int, CSubtitle*> m_subtitleCache;
+
+	CScreenLayoutAllocator m_sla;
+
+	CSize m_size;
+	CRect m_vidrect;
+
+	// temp variables, used when parsing the script
+	int m_time, m_delay;
+	int m_animStart, m_animEnd;
+	double m_animAccel;
+	int m_ktype, m_kstart, m_kend;
+	int m_nPolygon;
+	int m_polygonBaselineOffset;
+
+	void ParseEffect(CSubtitle* sub, CString str);
+	void ParseString(CSubtitle* sub, CStringW str, STSStyle& style);
+	void ParsePolygon(CSubtitle* sub, CStringW str, STSStyle& style);
+	bool ParseSSATag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org, bool fAnimate = false);
+	bool ParseHtmlTag(CSubtitle* sub, CStringW str, STSStyle& style, STSStyle& org);
+
+	double CalcAnimation(double dst, double src, bool fAnimate);
+
+	CSubtitle* GetSubtitle(int entry);
+
+protected:
+	virtual void OnChanged();
+
+public:
+	CRenderedTextSubtitle(CCritSec* pLock);
+	virtual ~CRenderedTextSubtitle();
+
+	virtual void Copy(CSimpleTextSubtitle& sts);
+	virtual void Empty();
+
+public:
+	bool Init(CSize size, CRect vidrect); // will call Deinit()
+	void Deinit();
+
+	DECLARE_IUNKNOWN
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicProvider
+	STDMETHODIMP_(POSITION) GetStartPosition(REFERENCE_TIME rt, double fps);
+	STDMETHODIMP_(POSITION) GetNext(POSITION pos);
+	STDMETHODIMP_(REFERENCE_TIME) GetStart(POSITION pos, double fps);
+	STDMETHODIMP_(REFERENCE_TIME) GetStop(POSITION pos, double fps);
+	STDMETHODIMP_(bool) IsAnimated(POSITION pos);
+	STDMETHODIMP Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox);
+
+	// IPersist
+	STDMETHODIMP GetClassID(CLSID* pClassID);
+
+	// ISubStream
+	STDMETHODIMP_(int) GetStreamCount();
+	STDMETHODIMP GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID);
+	STDMETHODIMP_(int) GetStream();
+	STDMETHODIMP SetStream(int iStream);
+	STDMETHODIMP Reload();
+};
diff --git a/vsfilter/subtitles/Rasterizer.cpp b/vsfilter/subtitles/Rasterizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..40e5eda12194fb2442f59e17951a0435cfef3e09
--- /dev/null
+++ b/vsfilter/subtitles/Rasterizer.cpp
@@ -0,0 +1,1154 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <string.h>
+#include <math.h>
+#include <vector>
+#include <algorithm>
+#include "Rasterizer.h"
+
+Rasterizer::Rasterizer() : mpPathTypes(NULL), mpPathPoints(NULL), mPathPoints(0), mpOverlayBuffer(NULL)
+{
+	mOverlayWidth = mOverlayHeight = 0;
+	mPathOffsetX = mPathOffsetY = 0;
+	mOffsetX = mOffsetY = 0;
+}
+
+Rasterizer::~Rasterizer()
+{
+	_TrashPath();
+	_TrashOverlay();
+}
+
+void Rasterizer::_TrashPath()
+{
+	delete [] mpPathTypes;
+	delete [] mpPathPoints;
+	mpPathTypes = NULL;
+	mpPathPoints = NULL;
+	mPathPoints = 0;
+}
+
+void Rasterizer::_TrashOverlay()
+{
+	delete [] mpOverlayBuffer;
+	mpOverlayBuffer = NULL;
+}
+
+void Rasterizer::_ReallocEdgeBuffer(int edges)
+{
+	mEdgeHeapSize = edges;
+	mpEdgeBuffer = (Edge*)realloc(mpEdgeBuffer, sizeof(Edge)*edges);
+}
+
+void Rasterizer::_EvaluateBezier(int ptbase, bool fBSpline)
+{
+	const POINT* pt0 = mpPathPoints + ptbase;
+	const POINT* pt1 = mpPathPoints + ptbase + 1;
+	const POINT* pt2 = mpPathPoints + ptbase + 2;
+	const POINT* pt3 = mpPathPoints + ptbase + 3;
+
+	double x0 = pt0->x;
+	double x1 = pt1->x;
+	double x2 = pt2->x;
+	double x3 = pt3->x;
+	double y0 = pt0->y;
+	double y1 = pt1->y;
+	double y2 = pt2->y;
+	double y3 = pt3->y;
+
+	double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
+
+	if(fBSpline)
+	{
+		// 1   [-1 +3 -3 +1]
+		// - * [+3 -6 +3  0]
+		// 6   [-3  0 +3  0]
+		//	   [+1 +4 +1  0]
+
+		double _1div6 = 1.0/6.0;
+
+		cx3 = _1div6*(-  x0+3*x1-3*x2+x3);
+		cx2 = _1div6*( 3*x0-6*x1+3*x2);
+		cx1 = _1div6*(-3*x0	   +3*x2);
+		cx0 = _1div6*(   x0+4*x1+1*x2);
+
+		cy3 = _1div6*(-  y0+3*y1-3*y2+y3);
+		cy2 = _1div6*( 3*y0-6*y1+3*y2);
+		cy1 = _1div6*(-3*y0     +3*y2);
+		cy0 = _1div6*(   y0+4*y1+1*y2);
+	}
+	else // bezier
+	{
+		// [-1 +3 -3 +1]
+		// [+3 -6 +3  0]
+		// [-3 +3  0  0]
+		// [+1  0  0  0]
+
+		cx3 = -  x0+3*x1-3*x2+x3;
+		cx2 =  3*x0-6*x1+3*x2;
+		cx1 = -3*x0+3*x1;
+		cx0 =    x0;
+
+		cy3 = -  y0+3*y1-3*y2+y3;
+		cy2 =  3*y0-6*y1+3*y2;
+		cy1 = -3*y0+3*y1;
+		cy0 =    y0;
+	}
+
+	//
+	// This equation is from Graphics Gems I.
+	//
+	// The idea is that since we're approximating a cubic curve with lines,
+	// any error we incur is due to the curvature of the line, which we can
+	// estimate by calculating the maximum acceleration of the curve.  For
+	// a cubic, the acceleration (second derivative) is a line, meaning that
+	// the absolute maximum acceleration must occur at either the beginning
+	// (|c2|) or the end (|c2+c3|).  Our bounds here are a little more
+	// conservative than that, but that's okay.
+	//
+	// If the acceleration of the parametric formula is zero (c2 = c3 = 0),
+	// that component of the curve is linear and does not incur any error.
+	// If a=0 for both X and Y, the curve is a line segment and we can
+	// use a step size of 1.
+
+	double maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
+	double maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
+
+	double maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
+	double h = 1.0;
+
+	if(maxaccel > 8.0) h = sqrt(8.0 / maxaccel);
+
+	if(!fFirstSet) {firstp.x = (LONG)cx0; firstp.y = (LONG)cy0; lastp = firstp; fFirstSet = true;}
+
+	for(double t = 0; t < 1.0; t += h)
+	{
+		double x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
+		double y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
+		_EvaluateLine(lastp.x, lastp.y, (int)x, (int)y);
+	}
+
+	double x = cx0 + cx1 + cx2 + cx3;
+	double y = cy0 + cy1 + cy2 + cy3;
+	_EvaluateLine(lastp.x, lastp.y, (int)x, (int)y);
+}
+
+void Rasterizer::_EvaluateLine(int pt1idx, int pt2idx)
+{
+	const POINT* pt1 = mpPathPoints + pt1idx;
+	const POINT* pt2 = mpPathPoints + pt2idx;
+
+	_EvaluateLine(pt1->x, pt1->y, pt2->x, pt2->y);
+}
+
+void Rasterizer::_EvaluateLine(int x0, int y0, int x1, int y1)
+{
+	if(lastp.x != x0 || lastp.y != y0)
+	{
+		_EvaluateLine(lastp.x, lastp.y, x0, y0);
+	}
+
+	if(!fFirstSet) {firstp.x = x0; firstp.y = y0; fFirstSet = true;}
+	lastp.x = x1; lastp.y = y1;
+
+	if(y1 > y0)	// down
+	{
+		__int64 xacc = (__int64)x0 << 13;
+
+		// prestep y0 down
+
+		int dy = y1 - y0;
+		int y = ((y0 + 3)&~7) + 4;
+		int iy = y >> 3;
+
+		y1 = (y1 - 5) >> 3;
+
+		if(iy <= y1)
+		{
+			__int64 invslope = (__int64(x1 - x0) << 16) / dy;
+
+			while(mEdgeNext + y1 + 1 - iy > mEdgeHeapSize)
+				_ReallocEdgeBuffer(mEdgeHeapSize*2);
+
+			xacc += (invslope * (y - y0)) >> 3;
+
+			while(iy <= y1)
+			{
+				int ix = (int)((xacc + 32768) >> 16);
+
+				mpEdgeBuffer[mEdgeNext].next = mpScanBuffer[iy];
+				mpEdgeBuffer[mEdgeNext].posandflag = ix*2 + 1;
+
+				mpScanBuffer[iy] = mEdgeNext++;
+
+				++iy;
+				xacc += invslope;
+			}
+		}
+	}
+	else if(y1 < y0) // up
+	{
+		__int64 xacc = (__int64)x1 << 13;
+
+		// prestep y1 down
+
+		int dy = y0 - y1;
+		int y = ((y1 + 3)&~7) + 4;
+		int iy = y >> 3;
+
+		y0 = (y0 - 5) >> 3;
+
+		if(iy <= y0)
+		{
+			__int64 invslope = (__int64(x0 - x1) << 16) / dy;
+
+			while(mEdgeNext + y0 + 1 - iy > mEdgeHeapSize)
+				_ReallocEdgeBuffer(mEdgeHeapSize*2);
+
+			xacc += (invslope * (y - y1)) >> 3;
+
+			while(iy <= y0)
+			{
+				int ix = (int)((xacc + 32768) >> 16);
+
+				mpEdgeBuffer[mEdgeNext].next = mpScanBuffer[iy];
+				mpEdgeBuffer[mEdgeNext].posandflag = ix*2;
+
+				mpScanBuffer[iy] = mEdgeNext++;
+
+				++iy;
+				xacc += invslope;
+			}
+		}
+	}
+}
+
+bool Rasterizer::BeginPath(HDC hdc)
+{
+	_TrashPath();
+
+	return !!::BeginPath(hdc);
+}
+
+bool Rasterizer::EndPath(HDC hdc)
+{
+	::CloseFigure(hdc);
+
+	if(::EndPath(hdc))
+	{
+		mPathPoints = GetPath(hdc, NULL, NULL, 0);
+
+		if(!mPathPoints)
+			return true;
+
+		mpPathTypes = (BYTE*)malloc(sizeof(BYTE) * mPathPoints);
+		mpPathPoints = (POINT*)malloc(sizeof(POINT) * mPathPoints);
+
+		if(mPathPoints == GetPath(hdc, mpPathPoints, mpPathTypes, mPathPoints))
+			return true;
+	}
+
+	::AbortPath(hdc);
+
+	return false;
+}
+
+bool Rasterizer::PartialBeginPath(HDC hdc, bool bClearPath)
+{
+	if(bClearPath)
+		_TrashPath();
+
+	return !!::BeginPath(hdc);
+}
+
+bool Rasterizer::PartialEndPath(HDC hdc, long dx, long dy)
+{
+	::CloseFigure(hdc);
+
+	if(::EndPath(hdc))
+	{
+		int nPoints;
+		BYTE* pNewTypes;
+		POINT* pNewPoints;
+
+		nPoints = GetPath(hdc, NULL, NULL, 0);
+
+		if(!nPoints)
+			return true;
+
+		pNewTypes = (BYTE*)realloc(mpPathTypes, (mPathPoints + nPoints) * sizeof(BYTE));
+		pNewPoints = (POINT*)realloc(mpPathPoints, (mPathPoints + nPoints) * sizeof(POINT));
+
+		if(pNewTypes)
+			mpPathTypes = pNewTypes;
+
+		if(pNewPoints)
+			mpPathPoints = pNewPoints;
+
+		BYTE* pTypes = new BYTE[nPoints];
+		POINT* pPoints = new POINT[nPoints];
+
+		if(pNewTypes && pNewPoints && nPoints == GetPath(hdc, pPoints, pTypes, nPoints))
+		{
+			for(int i = 0; i < nPoints; ++i)
+			{
+				mpPathPoints[mPathPoints + i].x = pPoints[i].x + dx;
+				mpPathPoints[mPathPoints + i].y = pPoints[i].y + dy;
+				mpPathTypes[mPathPoints + i] = pTypes[i];
+			}
+
+			mPathPoints += nPoints;
+
+			delete[] pTypes;
+			delete[] pPoints;
+			return true;
+		}
+		else
+			DebugBreak();
+
+		delete[] pTypes;
+		delete[] pPoints;
+	}
+
+	::AbortPath(hdc);
+
+	return false;
+}
+
+bool Rasterizer::ScanConvert()
+{
+	int lastmoveto = -1;
+	int i;
+
+	// Drop any outlines we may have.
+
+	mOutline.clear();
+	mWideOutline.clear();
+
+	// Determine bounding box
+
+	if(!mPathPoints)
+	{
+		mPathOffsetX = mPathOffsetY = 0;
+		mWidth = mHeight = 0;
+		return 0;
+	}
+
+	int minx = INT_MAX;
+	int miny = INT_MAX;
+	int maxx = INT_MIN;
+	int maxy = INT_MIN;
+
+	for(i=0; i<mPathPoints; ++i)
+	{
+		int ix = mpPathPoints[i].x;
+		int iy = mpPathPoints[i].y;
+
+		if(ix < minx) minx = ix;
+		if(ix > maxx) maxx = ix;
+		if(iy < miny) miny = iy;
+		if(iy > maxy) maxy = iy;
+	}
+
+	minx = (minx >> 3) & ~7;
+	miny = (miny >> 3) & ~7;
+	maxx = (maxx + 7) >> 3;
+	maxy = (maxy + 7) >> 3;
+
+	for(i=0; i<mPathPoints; ++i)
+	{
+		mpPathPoints[i].x -= minx*8;
+		mpPathPoints[i].y -= miny*8;
+	}
+
+	if(minx > maxx || miny > maxy)
+	{
+		mWidth = mHeight = 0;
+		mPathOffsetX = mPathOffsetY = 0;
+		_TrashPath();
+		return true;
+	}
+
+	mWidth = maxx + 1 - minx;
+	mHeight = maxy + 1 - miny;
+
+	mPathOffsetX = minx;
+	mPathOffsetY = miny;
+
+	// Initialize edge buffer.  We use edge 0 as a sentinel.
+
+	mEdgeNext = 1;
+	mEdgeHeapSize = 2048;
+	mpEdgeBuffer = (Edge*)malloc(sizeof(Edge)*mEdgeHeapSize);
+
+	// Initialize scanline list.
+
+	mpScanBuffer = new unsigned int[mHeight];
+	memset(mpScanBuffer, 0, mHeight*sizeof(unsigned int));
+
+	// Scan convert the outline.  Yuck, Bezier curves....
+
+	// Unfortunately, Windows 95/98 GDI has a bad habit of giving us text
+	// paths with all but the first figure left open, so we can't rely
+	// on the PT_CLOSEFIGURE flag being used appropriately.
+
+	fFirstSet = false;
+	firstp.x = firstp.y = 0;
+	lastp.x = lastp.y = 0;
+
+	for(i=0; i<mPathPoints; ++i)
+	{
+		BYTE t = mpPathTypes[i] & ~PT_CLOSEFIGURE;
+
+		switch(t)
+		{
+		case PT_MOVETO:
+			if(lastmoveto >= 0 && firstp != lastp)
+				_EvaluateLine(lastp.x, lastp.y, firstp.x, firstp.y);
+			lastmoveto = i;
+			fFirstSet = false;
+			lastp = mpPathPoints[i];
+			break;
+		case PT_MOVETONC:
+			break;
+		case PT_LINETO:
+			if(mPathPoints - (i-1) >= 2) _EvaluateLine(i-1, i);
+			break;
+		case PT_BEZIERTO:
+			if(mPathPoints - (i-1) >= 4) _EvaluateBezier(i-1, false);
+			i += 2;
+			break;
+		case PT_BSPLINETO:
+			if(mPathPoints - (i-1) >= 4) _EvaluateBezier(i-1, true);
+			i += 2;
+			break;
+		case PT_BSPLINEPATCHTO:
+			if(mPathPoints - (i-3) >= 4) _EvaluateBezier(i-3, true);
+			break;
+		}
+	}
+
+	if(lastmoveto >= 0 && firstp != lastp)
+		_EvaluateLine(lastp.x, lastp.y, firstp.x, firstp.y);
+
+	// Free the path since we don't need it anymore.
+
+	_TrashPath();
+
+	// Convert the edges to spans.  We couldn't do this before because some of
+	// the regions may have winding numbers >+1 and it would have been a pain
+	// to try to adjust the spans on the fly.  We use one heap to detangle
+	// a scanline's worth of edges from the singly-linked lists, and another
+	// to collect the actual scans.
+
+	std::vector<int> heap;
+
+	mOutline.reserve(mEdgeNext / 2);
+
+	__int64 y = 0;
+
+	for(y=0; y<mHeight; ++y)
+	{
+		int count = 0;
+
+		// Detangle scanline into edge heap.
+
+		for(unsigned ptr = (unsigned)(mpScanBuffer[y]&0xffffffff); ptr; ptr = mpEdgeBuffer[ptr].next)
+		{
+			heap.push_back(mpEdgeBuffer[ptr].posandflag);
+		}
+
+		// Sort edge heap.  Note that we conveniently made the opening edges
+		// one more than closing edges at the same spot, so we won't have any
+		// problems with abutting spans.
+
+		std::sort(heap.begin(), heap.end()/*begin() + heap.size()*/);
+
+		// Process edges and add spans.  Since we only check for a non-zero
+		// winding number, it doesn't matter which way the outlines go!
+
+		std::vector<int>::iterator itX1 = heap.begin();
+		std::vector<int>::iterator itX2 = heap.end(); // begin() + heap.size();
+
+		int x1, x2;
+
+		for(; itX1 != itX2; ++itX1)
+		{
+			int x = *itX1;
+
+			if(!count) 
+				x1 = (x>>1);
+
+			if(x&1) 
+				++count;
+			else 
+				--count;
+
+			if(!count)
+			{
+				x2 = (x>>1);
+
+				if(x2>x1)
+					mOutline.push_back(std::pair<__int64,__int64>((y<<32)+x1+0x4000000040000000i64, (y<<32)+x2+0x4000000040000000i64)); // G: damn Avery, this is evil! :)
+			}
+		}
+
+		heap.clear();
+	}
+
+	// Dump the edge and scan buffers, since we no longer need them.
+
+	free(mpEdgeBuffer);
+	delete [] mpScanBuffer;
+
+	// All done!
+
+	return true;
+}
+
+using namespace std;
+
+void Rasterizer::_OverlapRegion(tSpanBuffer& dst, tSpanBuffer& src, int dx, int dy)
+{
+	tSpanBuffer temp;
+
+	temp.reserve(dst.size() + src.size());
+
+	dst.swap(temp);
+
+	tSpanBuffer::iterator itA = temp.begin();
+	tSpanBuffer::iterator itAE = temp.end();
+	tSpanBuffer::iterator itB = src.begin();
+	tSpanBuffer::iterator itBE = src.end();
+
+	// Don't worry -- even if dy<0 this will still work! // G: hehe, the evil twin :)
+
+	unsigned __int64 offset1 = (((__int64)dy)<<32) - dx;
+	unsigned __int64 offset2 = (((__int64)dy)<<32) + dx;
+
+	while(itA != itAE && itB != itBE)
+	{
+		if((*itB).first + offset1 < (*itA).first)
+		{
+			// B span is earlier.  Use it.
+
+			unsigned __int64 x1 = (*itB).first + offset1;
+			unsigned __int64 x2 = (*itB).second + offset2;
+
+			++itB;
+
+			// B spans don't overlap, so begin merge loop with A first.
+
+			for(;;)
+			{
+				// If we run out of A spans or the A span doesn't overlap,
+				// then the next B span can't either (because B spans don't
+				// overlap) and we exit.
+
+				if(itA == itAE || (*itA).first > x2)
+					break;
+
+				do {x2 = _MAX(x2, (*itA++).second);}
+				while(itA != itAE && (*itA).first <= x2);
+
+				// If we run out of B spans or the B span doesn't overlap,
+				// then the next A span can't either (because A spans don't
+				// overlap) and we exit.
+
+				if(itB == itBE || (*itB).first + offset1 > x2)
+					break;
+
+				do {x2 = _MAX(x2, (*itB++).second + offset2);}
+				while(itB != itBE && (*itB).first + offset1 <= x2);
+			}
+
+			// Flush span.
+
+			dst.push_back(tSpan(x1, x2));	
+		}
+		else
+		{
+			// A span is earlier.  Use it.
+
+			unsigned __int64 x1 = (*itA).first;
+			unsigned __int64 x2 = (*itA).second;
+
+			++itA;
+
+			// A spans don't overlap, so begin merge loop with B first.
+
+			for(;;)
+			{
+				// If we run out of B spans or the B span doesn't overlap,
+				// then the next A span can't either (because A spans don't
+				// overlap) and we exit.
+
+				if(itB == itBE || (*itB).first + offset1 > x2)
+					break;
+
+				do {x2 = _MAX(x2, (*itB++).second + offset2);}
+				while(itB != itBE && (*itB).first + offset1 <= x2);
+
+				// If we run out of A spans or the A span doesn't overlap,
+				// then the next B span can't either (because B spans don't
+				// overlap) and we exit.
+
+				if(itA == itAE || (*itA).first > x2)
+					break;
+
+				do {x2 = _MAX(x2, (*itA++).second);}
+				while(itA != itAE && (*itA).first <= x2);
+			}
+
+			// Flush span.
+
+			dst.push_back(tSpan(x1, x2));	
+		}
+	}
+
+	// Copy over leftover spans.
+
+	while(itA != itAE)
+		dst.push_back(*itA++);
+
+	while(itB != itBE)
+	{
+		dst.push_back(tSpan((*itB).first + offset1, (*itB).second + offset2));	
+		++itB;
+	}
+}
+
+bool Rasterizer::CreateWidenedRegion(int r)
+{
+	if(r < 0) r = 0;
+
+	for(int y = -r; y <= r; ++y)
+	{
+		int x = (int)(0.5 + sqrt(float(r*r - y*y)));
+
+		_OverlapRegion(mWideOutline, mOutline, x, y);
+	}
+
+	mWideBorder = r;
+
+	return true;
+}
+
+void Rasterizer::DeleteOutlines()
+{
+	mWideOutline.clear();
+	mOutline.clear();
+}
+
+bool Rasterizer::Rasterize(int xsub, int ysub, bool fBlur)
+{
+	_TrashOverlay();
+
+	if(!mWidth || !mHeight)
+	{
+		mOverlayWidth = mOverlayHeight = 0;
+		return true;
+	}
+
+	xsub &= 7;
+	ysub &= 7;
+
+	int width = mWidth + xsub;
+	int height = mHeight + ysub;
+
+	mOffsetX = mPathOffsetX - xsub;
+	mOffsetY = mPathOffsetY - ysub;
+
+	mWideBorder = (mWideBorder+7)&~7;
+
+	if(!mWideOutline.empty())
+	{
+		width += 2*mWideBorder;
+		height += 2*mWideBorder;
+
+		xsub += mWideBorder;
+		ysub += mWideBorder;
+
+		mOffsetX -= mWideBorder;
+		mOffsetY -= mWideBorder;
+	}
+
+	mOverlayWidth = ((width+7)>>3) + 1;
+	mOverlayHeight = ((height+7)>>3) + 1;
+
+	mpOverlayBuffer = new byte[2 * mOverlayWidth * mOverlayHeight];
+	memset(mpOverlayBuffer, 0, 2 * mOverlayWidth * mOverlayHeight);
+
+	// Are we doing a border?
+
+	tSpanBuffer* pOutline[2] = {&mOutline, &mWideOutline};
+
+	for(int i = countof(pOutline)-1; i >= 0; i--)
+	{
+		tSpanBuffer::iterator it = pOutline[i]->begin();
+		tSpanBuffer::iterator itEnd = pOutline[i]->end();
+
+		for(; it!=itEnd; ++it)
+		{
+			int y = (int)(((*it).first >> 32) - 0x40000000 + ysub);
+			int x1 = (int)(((*it).first & 0xffffffff) - 0x40000000 + xsub);
+			int x2 = (int)(((*it).second & 0xffffffff) - 0x40000000 + xsub);
+
+			if(x2 > x1)
+			{
+				int first = x1>>3;
+				int last = (x2-1)>>3;
+				byte* dst = mpOverlayBuffer + 2*(mOverlayWidth*(y>>3) + first) + i;
+
+				if(first == last)
+					*dst += x2-x1;
+				else
+				{
+					*dst += ((first+1)<<3) - x1;
+					dst += 2;
+
+					while(++first < last)
+					{
+						*dst += 0x08;
+						dst += 2;
+					}
+
+					*dst += x2 - (last<<3);
+				}
+			}
+		}
+	}
+
+	if(fBlur && mOverlayWidth >= 3 && mOverlayHeight >= 3)
+	{
+		int pitch = mOverlayWidth*2;
+
+		byte* tmp = new byte[pitch*mOverlayHeight];
+		if(!tmp) return(false);
+
+		memcpy(tmp, mpOverlayBuffer, pitch*mOverlayHeight);
+
+		int border = !mWideOutline.empty() ? 1 : 0;
+
+		for(int j = 1; j < mOverlayHeight-1; j++)
+		{
+			byte* src = tmp + pitch*j + 2 + border;
+			byte* dst = mpOverlayBuffer + pitch*j + 2 + border;
+
+			for(int i = 1; i < mOverlayWidth-1; i++, src+=2, dst+=2)
+			{
+				*dst = (src[-2-pitch] + (src[-pitch]<<1) + src[+2-pitch]
+					+ (src[-2]<<1) + (src[0]<<2) + (src[+2]<<1)
+					+ src[-2+pitch] + (src[+pitch]<<1) + src[+2+pitch]) >> 4;
+			}
+		}
+
+		delete [] tmp;
+	}
+
+	return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/*#define pixmix(s)																 \
+	int a = (((s)*(color>>24))>>6)&0xff;										 \
+	int ia = 256-a;																 \
+																				 \
+	dst[wt] = ((((dst[wt]&0x00ff00ff)*ia + (color&0x00ff00ff)*a)&0xff00ff00)>>8) \
+			| ((((dst[wt]&0x0000ff00)*ia + (color&0x0000ff00)*a)&0x00ff0000)>>8) \
+			| ((((dst[wt]>>8)&0x00ff0000)*ia)&0xff000000);
+
+#define pixmix2(s)																 \
+	int a = ((((s)*(am[wt]))*(color>>24))>>12)&0xff;							 \
+	int ia = 256-a;																 \
+																				 \
+	dst[wt] = ((((dst[wt]&0x00ff00ff)*ia + (color&0x00ff00ff)*a)&0xff00ff00)>>8) \
+			| ((((dst[wt]&0x0000ff00)*ia + (color&0x0000ff00)*a)&0x00ff0000)>>8) \
+			| ((((dst[wt]>>8)&0x00ff0000)*ia)&0xff000000);*/
+
+static __forceinline void pixmix(DWORD *dst, DWORD color, DWORD alpha)
+{
+	int a = (((alpha)*(color>>24))>>12)&0xff;
+	int ia = 256-a;
+
+	*dst = ((((*dst&0x00ff00ff)*ia + (color&0x00ff00ff)*a)&0xff00ff00)>>8)
+			| ((((*dst&0x0000ff00)*ia + (color&0x0000ff00)*a)&0x00ff0000)>>8)
+			| ((((*dst>>8)&0x00ff0000)*ia)&0xff000000);
+}
+
+#include <xmmintrin.h>
+#include <emmintrin.h>
+
+static __forceinline void pixmix_sse2(DWORD* dst, DWORD color, DWORD alpha)
+{
+	alpha = ((alpha * (color>>24)) >> 12) & 0xff;
+	color &= 0xffffff;
+
+	__m128i zero = _mm_setzero_si128();
+	__m128i a = _mm_set1_epi32((alpha << 16) | (0x100 - alpha));
+	__m128i d = _mm_unpacklo_epi8(_mm_cvtsi32_si128(*dst), zero);
+	__m128i s = _mm_unpacklo_epi8(_mm_cvtsi32_si128(color), zero);
+	__m128i r = _mm_unpacklo_epi16(d, s);
+
+	r = _mm_madd_epi16(r, a);
+	r = _mm_srli_epi32(r, 8);
+	r = _mm_packs_epi32(r, r);
+	r = _mm_packus_epi16(r, r);
+
+	*dst = (DWORD)_mm_cvtsi128_si32(r);
+}
+
+#include "../dsutil/vd.h"
+
+static const __int64 _00ff00ff00ff00ff = 0x00ff00ff00ff00ffi64;
+
+CRect Rasterizer::Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int xsub, int ysub, const long* switchpts, bool fBody, bool fBorder)
+{
+	CRect bbox(0, 0, 0, 0);
+
+	if(!switchpts || !fBody && !fBorder) return(bbox);
+
+	// clip
+
+	CRect r(0, 0, spd.w, spd.h);
+	r &= clipRect;
+
+	int x = (xsub + mOffsetX + 4)>>3;
+	int y = (ysub + mOffsetY + 4)>>3;
+	int w = mOverlayWidth;
+	int h = mOverlayHeight;
+	int xo = 0, yo = 0;
+
+	if(x < r.left) {xo = r.left-x; w -= r.left-x; x = r.left;}
+	if(y < r.top) {yo = r.top-y; h -= r.top-y; y = r.top;}
+	if(x+w > r.right) w = r.right-x;
+	if(y+h > r.bottom) h = r.bottom-y;
+
+	if(w <= 0 || h <= 0) return(bbox);
+
+	bbox.SetRect(x, y, x+w, y+h);
+	bbox &= CRect(0, 0, spd.w, spd.h);
+
+	// draw
+
+	const byte* src = mpOverlayBuffer + 2*(mOverlayWidth * yo + xo);
+	const byte* s = fBorder ? (src+1) : src;
+	const byte* am = pAlphaMask + spd.w * y + x;
+	unsigned long* dst = (unsigned long *)((char *)spd.bits + spd.pitch * y) + x;
+
+	unsigned long color = switchpts[0];
+
+	bool fSSE2 = !!(g_cpuid.m_flags & CCpuID::sse2);
+
+	while(h--)
+	{
+		if(!pAlphaMask)
+		{
+			if(switchpts[1] == 0xffffffff)
+			{
+				if(fBody)
+				{
+					const byte* s = fBorder?(src+1):src;
+/*
+					for(int wt=0; wt<w; ++wt)
+					{
+						pixmix(s[wt*2]);
+					}
+*/
+					/*__asm
+					{
+						pxor		mm0, mm0
+
+						mov			eax, color
+						mov			ebx, eax
+						and			eax, 0x00ffffff
+						movd		mm3, eax
+						punpcklbw	mm3, mm0 // mm3 = color&0xffffff
+						shr			ebx, 24 // bl = color>>24
+
+						mov			ecx, w
+						mov			esi, s
+						mov			edi, dst
+
+				pixmixloop:
+
+						xor			eax, eax
+						mov			al, [esi] // s[wt*2]
+						imul		eax, ebx
+						shr			eax, 6
+						and			eax, 0xff
+						imul		eax, 0x01010101
+
+						movd		mm1, eax
+						movq		mm2, _00ff00ff00ff00ff
+						punpcklbw	mm1, mm0 // a
+						psubsw		mm2, mm1 // ia
+						pmullw		mm1, mm3 // a *= color
+						movd		mm4, [edi]
+						punpcklbw	mm4, mm0 // dst[wt]
+						pmullw		mm2, mm4 // ia *= dst[wt]
+						paddsw		mm1, mm2 // a*color += ia*dst[wt]
+						psrlw		mm1, 8
+						packuswb	mm1, mm1
+						movd		[edi], mm1
+
+						add			esi, 2
+						add			edi, 4
+						loop		pixmixloop
+					}*/
+					if(fSSE2)
+						for(int wt=0; wt<w; ++wt)
+							pixmix_sse2(&dst[wt], color, s[wt*2]<<6);
+					else
+						for(int wt=0; wt<w; ++wt)
+							pixmix(&dst[wt], color, s[wt*2]<<6);
+				}
+				else
+				{
+/*					for(int wt=0; wt<w; ++wt)
+					{
+						pixmix(src[wt*2+1]-src[wt*2]);
+					}
+*/
+					/*__asm
+					{
+						pxor		mm0, mm0
+
+						mov			eax, color
+						mov			ebx, eax
+						and			eax, 0x00ffffff
+						movd		mm3, eax
+						punpcklbw	mm3, mm0 // mm3 = color&0xffffff
+						shr			ebx, 24 // bl = color>>24
+
+						mov			ecx, w
+						mov			esi, src
+						mov			edi, dst
+
+					pixmixloop2:
+
+						xor			eax, eax
+						mov			al, [esi+1] // src[wt*2+1]-src[wt*2]
+						sub			al, [esi]
+						imul		eax, ebx
+						shr			eax, 6
+						and			eax, 0xff
+						imul		eax, 0x01010101
+
+						movd		mm1, eax
+						movq		mm2, _00ff00ff00ff00ff
+						punpcklbw	mm1, mm0 // a
+						psubsw		mm2, mm1 // ia
+						pmullw		mm1, mm3 // a *= color
+						movd		mm4, [edi]
+						punpcklbw	mm4, mm0 // dst[wt]
+						pmullw		mm2, mm4 // ia *= dst[wt]
+						paddsw		mm1, mm2 // a*color += ia*dst[wt]
+						psrlw		mm1, 8
+						packuswb	mm1, mm1
+						movd		[edi], mm1
+
+						add			esi, 2
+						add			edi, 4
+						loop		pixmixloop2
+					}*/
+					if(fSSE2)
+						for(int wt=0; wt<w; ++wt)
+							pixmix_sse2(&dst[wt], color, (src[wt*2+1] - src[wt*2])<<6);
+					else
+						for(int wt=0; wt<w; ++wt)
+							pixmix(&dst[wt], color, (src[wt*2+1] - src[wt*2])<<6);
+				}
+				//__asm emms;
+			}
+			else
+			{
+				const long *sw = switchpts;
+
+				if(fBody)
+				{
+					/*const byte* s = fBorder?(src+1):src;
+					
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1])
+						{
+							while(wt+xo >= sw[1]) sw += 2;
+							color = sw[-2];
+						}
+
+						pixmix(s[wt*2]);
+					}*/
+					if(fSSE2) 
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];}
+						pixmix_sse2(&dst[wt], color, s[wt*2]<<6);
+					}
+					else
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];}
+						pixmix(&dst[wt], color, s[wt*2]<<6);
+					}
+				}
+				else
+				{
+					/*for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1])
+						{
+							while(wt+xo >= sw[1]) sw += 2;
+							color = sw[-2];
+						}
+
+						pixmix(src[wt*2+1]-src[wt*2]);
+					}*/
+					if(fSSE2) 
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];} 
+						pixmix_sse2(&dst[wt], color, (src[wt*2+1] - src[wt*2])<<6);
+					}
+					else
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {while(wt+xo >= sw[1]) sw += 2; color = sw[-2];} 
+						pixmix(&dst[wt], color, (src[wt*2+1] - src[wt*2])<<6);
+					}
+				}
+			}
+		}
+		else
+		{
+			if(switchpts[1] == 0xffffffff)
+			{
+				if(fBody)
+				{
+					/*const byte* s = fBorder?(src+1):src;
+
+					for(int wt=0; wt<w; ++wt)
+					{
+						pixmix2(s[wt*2]);
+					}*/
+					if(fSSE2)
+						for(int wt=0; wt<w; ++wt)
+							pixmix_sse2(&dst[wt], color, s[wt*2] * am[wt]);
+					else
+						for(int wt=0; wt<w; ++wt)
+							pixmix(&dst[wt], color, s[wt*2] * am[wt]);
+				}
+				else
+				{
+					/*for(int wt=0; wt<w; ++wt)
+					{
+						pixmix2(src[wt*2+1]-src[wt*2]);
+					}*/
+					if(fSSE2)
+						for(int wt=0; wt<w; ++wt)
+							pixmix_sse2(&dst[wt], color, (src[wt*2+1] - src[wt*2]) * am[wt]);
+					else
+						for(int wt=0; wt<w; ++wt)
+							pixmix(&dst[wt], color, (src[wt*2+1] - src[wt*2]) * am[wt]);
+				}
+			}
+			else
+			{
+				const long *sw = switchpts;
+
+				if(fBody)
+				{
+					/*const byte* s = fBorder?(src+1):src;
+					
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1])
+						{
+							while(wt+xo >= sw[1]) sw += 2;
+							color = sw[-2];
+						}
+
+						pixmix2(s[wt*2]);
+					}*/
+					if(fSSE2) 
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {
+							while(wt+xo >= sw[1])
+								sw += 2; color = sw[-2];
+						}
+						pixmix_sse2(&dst[wt], color, s[wt*2] * am[wt]);
+					}
+					else
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {
+							while(wt+xo >= sw[1])
+								sw += 2; color = sw[-2];
+						}
+						pixmix(&dst[wt], color, s[wt*2] * am[wt]);
+					}
+				}
+				else
+				{
+					/*for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1])
+						{
+							while(wt+xo >= sw[1]) sw += 2;
+							color = sw[-2];
+						}
+
+						pixmix2(src[wt*2+1]-src[wt*2]);
+					}*/
+					if(fSSE2) 
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {
+							while(wt+xo >= sw[1])
+								sw += 2; color = sw[-2];
+						} 
+						pixmix_sse2(&dst[wt], color, (src[wt*2+1] - src[wt*2]) * am[wt]);
+					}
+					else
+					for(int wt=0; wt<w; ++wt)
+					{
+						if(wt+xo >= sw[1]) {
+							while(wt+xo >= sw[1])
+								sw += 2; color = sw[-2];
+						} 
+						pixmix(&dst[wt], color, (src[wt*2+1] - src[wt*2]) * am[wt]);
+					}
+				}
+			}
+		}
+
+		src += 2*mOverlayWidth;
+		s += 2*mOverlayWidth;
+		am += spd.w;
+		dst = (unsigned long *)((char *)dst + spd.pitch);
+	}
+
+	return bbox;
+}
diff --git a/vsfilter/subtitles/Rasterizer.h b/vsfilter/subtitles/Rasterizer.h
new file mode 100644
index 0000000000000000000000000000000000000000..c6d6186c4fcca712de1f272bdc4d6ae12d7be45a
--- /dev/null
+++ b/vsfilter/subtitles/Rasterizer.h
@@ -0,0 +1,91 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <vector>
+#include "..\SubPic\ISubPic.h"
+
+#define PT_MOVETONC 0xfe
+#define PT_BSPLINETO 0xfc
+#define PT_BSPLINEPATCHTO 0xfa 
+
+class Rasterizer
+{
+	bool fFirstSet;
+	CPoint firstp, lastp;
+
+protected:
+	BYTE* mpPathTypes;
+	POINT* mpPathPoints;
+	int mPathPoints;
+
+private:
+	int mWidth, mHeight;
+
+	typedef std::pair<unsigned __int64, unsigned __int64> tSpan;
+	typedef std::vector<tSpan> tSpanBuffer;
+
+	tSpanBuffer mOutline;
+	tSpanBuffer mWideOutline;
+	int mWideBorder;
+
+	struct Edge {
+		int next;
+		int posandflag;
+	} *mpEdgeBuffer;
+	unsigned mEdgeHeapSize;
+	unsigned mEdgeNext;
+
+	unsigned int* mpScanBuffer;
+
+	typedef unsigned char byte;
+
+protected:
+	byte *mpOverlayBuffer;
+	int mOverlayWidth, mOverlayHeight;
+	int mPathOffsetX, mPathOffsetY;
+	int mOffsetX, mOffsetY;
+
+private:
+	void _TrashPath();
+	void _TrashOverlay();
+	void _ReallocEdgeBuffer(int edges);
+	void _EvaluateBezier(int ptbase, bool fBSpline);
+	void _EvaluateLine(int pt1idx, int pt2idx);
+	void _EvaluateLine(int x0, int y0, int x1, int y1);
+	static void _OverlapRegion(tSpanBuffer& dst, tSpanBuffer& src, int dx, int dy);
+
+public:
+	Rasterizer();
+	virtual ~Rasterizer();
+
+	bool BeginPath(HDC hdc);
+	bool EndPath(HDC hdc);
+	bool PartialBeginPath(HDC hdc, bool bClearPath);
+	bool PartialEndPath(HDC hdc, long dx, long dy);
+	bool ScanConvert();
+	bool CreateWidenedRegion(int border);
+	void DeleteOutlines();
+	bool Rasterize(int xsub, int ysub, bool fBlur);
+	CRect Draw(SubPicDesc& spd, CRect& clipRect, byte* pAlphaMask, int xsub, int ysub, const long* switchpts, bool fBody, bool fBorder);
+};
+
diff --git a/vsfilter/subtitles/SSF.cpp b/vsfilter/subtitles/SSF.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f490d69b1d66a6de2c822ebfe1fd5a37d539aa5f
--- /dev/null
+++ b/vsfilter/subtitles/SSF.cpp
@@ -0,0 +1,239 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ *  TODO: 
+ *  - fill effect
+ *  - outline bkg still very slow
+ *
+ */
+
+#include "stdafx.h"
+#include <xmmintrin.h>
+#include <emmintrin.h>
+#include "SSF.h"
+#include "..\subpic\MemSubPic.h"
+
+namespace ssf
+{
+	CRenderer::CRenderer(CCritSec* pLock)
+		: ISubPicProviderImpl(pLock)
+	{
+	}
+
+	CRenderer::~CRenderer()
+	{
+	}
+
+	bool CRenderer::Open(CString fn, CString name)
+	{
+		m_fn.Empty();
+		m_name.Empty();
+		m_file.Free();
+		m_renderer.Free();
+
+		if(name.IsEmpty())
+		{
+			CString str = fn;
+			str.Replace('\\', '/');
+			name = str.Left(str.ReverseFind('.'));
+			name = name.Mid(name.ReverseFind('/')+1);
+			name = name.Mid(name.ReverseFind('.')+1);
+		}
+
+		try
+		{
+			if(Open(FileInputStream(fn), name)) 
+			{
+				m_fn = fn;
+				return true;
+			}
+		}
+		catch(Exception& e)
+		{
+			TRACE(_T("%s\n"), e.ToString());
+		}
+
+		return false;	
+	}
+
+	bool CRenderer::Open(InputStream& s, CString name)
+	{
+		m_fn.Empty();
+		m_name.Empty();
+		m_file.Free();
+		m_renderer.Free();
+
+		try
+		{
+			m_file.Attach(new SubtitleFile());
+			m_file->Parse(s);
+			m_renderer.Attach(new Renderer());
+			m_name = name;
+			return true;
+		}
+		catch(Exception& e)
+		{
+			TRACE(_T("%s\n"), e.ToString());
+		}
+
+		return false;
+	}
+
+	void CRenderer::Append(REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, LPCWSTR str)
+	{
+		if(!m_file) return;
+
+		try
+		{
+			m_file->Append(ssf::WCharInputStream(str), (float)rtStart / 10000000, (float)rtStop / 10000000);
+		}
+		catch(Exception& e)
+		{
+			TRACE(_T("%s\n"), e.ToString());
+		}
+	}
+
+	STDMETHODIMP CRenderer::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+	{
+		CheckPointer(ppv, E_POINTER);
+		*ppv = NULL;
+
+		return 
+			QI(IPersist)
+			QI(ISubStream)
+			QI(ISubPicProvider)
+			__super::NonDelegatingQueryInterface(riid, ppv);
+	}
+
+	// ISubPicProvider
+
+	STDMETHODIMP_(POSITION) CRenderer::GetStartPosition(REFERENCE_TIME rt, double fps)
+	{
+		size_t k;
+		return m_file && m_file->m_segments.Lookup((float)rt/10000000, k) ? (POSITION)(++k) : NULL;
+	}
+
+	STDMETHODIMP_(POSITION) CRenderer::GetNext(POSITION pos)
+	{
+		size_t k = (size_t)pos;
+		return m_file && m_file->m_segments.GetSegment(k) ? (POSITION)(++k) : NULL;
+	}
+
+	STDMETHODIMP_(REFERENCE_TIME) CRenderer::GetStart(POSITION pos, double fps)
+	{
+		size_t k = (size_t)pos-1;
+		const SubtitleFile::Segment* s = m_file ? m_file->m_segments.GetSegment(k) : NULL;
+		return s ? (REFERENCE_TIME)(s->m_start*10000000) : 0;
+	}
+
+	STDMETHODIMP_(REFERENCE_TIME) CRenderer::GetStop(POSITION pos, double fps)
+	{
+		CheckPointer(m_file, 0);
+
+		size_t k = (size_t)pos-1;
+		const SubtitleFile::Segment* s = m_file ? m_file->m_segments.GetSegment(k) : NULL;
+		return s ? (REFERENCE_TIME)(s->m_stop*10000000) : 0;
+	}
+
+	STDMETHODIMP_(bool) CRenderer::IsAnimated(POSITION pos)
+	{
+		return true;
+	}
+
+	STDMETHODIMP CRenderer::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
+	{
+		CheckPointer(m_file, E_UNEXPECTED);
+		CheckPointer(m_renderer, E_UNEXPECTED);	
+
+		if(spd.type != MSP_RGB32) return E_INVALIDARG;
+
+		CAutoLock csAutoLock(m_pLock);
+
+		CRect bbox2;
+		bbox2.SetRectEmpty();
+
+		CAutoPtrList<Subtitle> subs;
+		m_file->Lookup((float)rt/10000000, subs);
+
+		m_renderer->NextSegment(subs);
+
+		POSITION pos = subs.GetHeadPosition();
+		while(pos)
+		{
+			const Subtitle* s = subs.GetNext(pos);
+			const RenderedSubtitle* rs = m_renderer->Lookup(s, CSize(spd.w, spd.h), spd.vidrect);
+			if(rs) bbox2 |= rs->Draw(spd);
+		}
+
+		bbox = bbox2 & CRect(0, 0, spd.w, spd.h);
+
+		return S_OK;
+	}
+
+	// IPersist
+
+	STDMETHODIMP CRenderer::GetClassID(CLSID* pClassID)
+	{
+		return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
+	}
+
+	// ISubStream
+
+	STDMETHODIMP_(int) CRenderer::GetStreamCount()
+	{
+		return 1;
+	}
+
+	STDMETHODIMP CRenderer::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
+	{
+		if(iStream != 0) return E_INVALIDARG;
+
+		if(ppName)
+		{
+			if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
+				return E_OUTOFMEMORY;
+
+			wcscpy(*ppName, CStringW(m_name));
+		}
+
+		if(pLCID)
+		{
+			*pLCID = 0; // TODO
+		}
+
+		return S_OK;
+	}
+
+	STDMETHODIMP_(int) CRenderer::GetStream()
+	{
+		return 0;
+	}
+
+	STDMETHODIMP CRenderer::SetStream(int iStream)
+	{
+		return iStream == 0 ? S_OK : E_FAIL;
+	}
+
+	STDMETHODIMP CRenderer::Reload()
+	{
+		CAutoLock csAutoLock(m_pLock);
+
+		return !m_fn.IsEmpty() && Open(m_fn, m_name) ? S_OK : E_FAIL;
+	}
+}
\ No newline at end of file
diff --git a/vsfilter/subtitles/SSF.h b/vsfilter/subtitles/SSF.h
new file mode 100644
index 0000000000000000000000000000000000000000..3bca2e242aab1f6b688236ccbee1e8e6e7b75c8b
--- /dev/null
+++ b/vsfilter/subtitles/SSF.h
@@ -0,0 +1,70 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "..\subpic\ISubPic.h"
+#include "..\libssf\SubtitleFile.h"
+#include "..\libssf\Renderer.h"
+
+#pragma once
+
+namespace ssf
+{
+	[uuid("E0593632-0AB7-47CA-8BE1-E9D2A6A4825E")]
+	class CRenderer : public ISubPicProviderImpl, public ISubStream
+	{
+		CString m_fn, m_name;
+		CAutoPtr<SubtitleFile> m_file;
+		CAutoPtr<Renderer> m_renderer;
+
+	public:
+		CRenderer(CCritSec* pLock);
+		virtual ~CRenderer();
+
+		bool Open(CString fn, CString name = _T(""));
+		bool Open(InputStream& s, CString name);
+
+		void Append(REFERENCE_TIME rtStart, REFERENCE_TIME rtStop, LPCWSTR str);
+
+		DECLARE_IUNKNOWN
+		STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+		// ISubPicProvider
+		STDMETHODIMP_(POSITION) GetStartPosition(REFERENCE_TIME rt, double fps);
+		STDMETHODIMP_(POSITION) GetNext(POSITION pos);
+		STDMETHODIMP_(REFERENCE_TIME) GetStart(POSITION pos, double fps);
+		STDMETHODIMP_(REFERENCE_TIME) GetStop(POSITION pos, double fps);
+		STDMETHODIMP_(bool) IsAnimated(POSITION pos);
+		STDMETHODIMP Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox);
+
+		// IPersist
+		STDMETHODIMP GetClassID(CLSID* pClassID);
+
+		// ISubStream
+		STDMETHODIMP_(int) GetStreamCount();
+		STDMETHODIMP GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID);
+		STDMETHODIMP_(int) GetStream();
+		STDMETHODIMP SetStream(int iStream);
+		STDMETHODIMP Reload();
+	};
+
+}
\ No newline at end of file
diff --git a/vsfilter/subtitles/STS.cpp b/vsfilter/subtitles/STS.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af8c1d4c77d4cbd23425e605b95bd39bb1c558e0
--- /dev/null
+++ b/vsfilter/subtitles/STS.cpp
@@ -0,0 +1,3030 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "STS.h"
+#include <atlbase.h>
+
+// gathered from http://www.netwave.or.jp/~shikai/shikai/shcolor.htm
+
+struct htmlcolor {TCHAR* name; DWORD color;} hmtlcolors[] = 
+{
+	{_T("white"), 0xffffff}, 
+	{_T("whitesmoke"), 0xf5f5f5}, 
+	{_T("ghostwhite"), 0xf8f8ff}, 
+	{_T("snow"), 0xfffafa}, 
+	{_T("gainsboro"), 0xdcdcdc}, 
+	{_T("lightgrey"), 0xd3d3d3}, 
+	{_T("silver"), 0xc0c0c0}, 
+	{_T("darkgray"), 0xa9a9a9}, 
+	{_T("gray"), 0x808080}, 
+	{_T("dimgray"), 0x696969}, 
+	{_T("lightslategray"), 0x778899}, 
+	{_T("slategray"), 0x708090}, 
+	{_T("darkslategray"), 0x2f4f4f}, 
+	{_T("black"), 0x000000}, 
+
+	{_T("azure"), 0xf0ffff}, 
+	{_T("aliceblue"), 0xf0f8ff}, 
+	{_T("mintcream"), 0xf5fffa}, 
+	{_T("honeydew"), 0xf0fff0}, 
+	{_T("lightcyan"), 0xe0ffff}, 
+	{_T("paleturqoise"), 0xafeeee}, 
+	{_T("powderblue"), 0xb0e0e6}, 
+	{_T("lightblue"), 0xadd8ed}, 
+	{_T("lightsteelblue"), 0xb0c4de}, 
+	{_T("skyblue"), 0x87ceeb}, 
+	{_T("lightskyblue"), 0x87cefa}, 
+	{_T("cyan"), 0x00ffff}, 
+	{_T("aqua"), 0x00ff80}, 
+	{_T("deepskyblue"), 0x00bfff}, 
+	{_T("aquamarine"), 0x7fffd4}, 
+	{_T("turquoise"), 0x40e0d0}, 
+	{_T("darkturquoise"), 0x00ced1}, 
+	{_T("lightseagreen"), 0x20b2aa}, 
+	{_T("mediumturquoise"), 0x40e0dd}, 
+	{_T("mediumaquamarine"), 0x66cdaa}, 
+	{_T("cadetblue"), 0x5f9ea0}, 
+	{_T("teal"), 0x008080}, 
+	{_T("darkcyan"), 0x008b8b}, 
+	{_T("comflowerblue"), 0x6495ed}, 
+	{_T("dodgerblue"), 0x1e90ff}, 
+	{_T("steelblue"), 0x4682b4}, 
+	{_T("royalblue"), 0x4169e1}, 
+	{_T("blue"), 0x0000ff}, 
+	{_T("mediumblue"), 0x0000cd}, 
+	{_T("mediumslateblue"), 0x7b68ee}, 
+	{_T("slateblue"), 0x6a5acd}, 
+	{_T("darkslateblue"), 0x483d8b}, 
+	{_T("darkblue"), 0x00008b}, 
+	{_T("midnightblue"), 0x191970}, 
+	{_T("navy"), 0x000080}, 
+
+	{_T("palegreen"), 0x98fb98}, 
+	{_T("lightgreen"), 0x90ee90}, 
+	{_T("mediumspringgreen"), 0x00fa9a}, 
+	{_T("springgreen"), 0x00ff7f}, 
+	{_T("chartreuse"), 0x7fff00}, 
+	{_T("lawngreen"), 0x7cfc00}, 
+	{_T("lime"), 0x00ff00}, 
+	{_T("limegreen"), 0x32cd32}, 
+	{_T("greenyellow"), 0xadff2f}, 
+	{_T("yellowgreen"), 0x9acd32}, 
+	{_T("darkseagreen"), 0x8fbc8f}, 
+	{_T("mediumseagreen"), 0x3cb371}, 
+	{_T("seagreen"), 0x2e8b57}, 
+	{_T("olivedrab"), 0x6b8e23}, 
+	{_T("forestgreen"), 0x228b22}, 
+	{_T("green"), 0x008000}, 
+	{_T("darkkhaki"), 0xbdb76b}, 
+	{_T("olive"), 0x808000}, 
+	{_T("darkolivegreen"), 0x556b2f}, 
+	{_T("darkgreen"), 0x006400}, 
+ 
+	{_T("floralwhite"), 0xfffaf0}, 
+	{_T("seashell"), 0xfff5ee}, 
+	{_T("ivory"), 0xfffff0}, 
+	{_T("beige"), 0xf5f5dc}, 
+	{_T("cornsilk"), 0xfff8dc}, 
+	{_T("lemonchiffon"), 0xfffacd}, 
+	{_T("lightyellow"), 0xffffe0}, 
+	{_T("lightgoldenrodyellow"), 0xfafad2}, 
+	{_T("papayawhip"), 0xffefd5}, 
+	{_T("blanchedalmond"), 0xffedcd}, 
+	{_T("palegoldenrod"), 0xeee8aa}, 
+	{_T("khaki"), 0xf0eb8c}, 
+	{_T("bisque"), 0xffe4c4}, 
+	{_T("moccasin"), 0xffe4b5}, 
+	{_T("navajowhite"), 0xffdead}, 
+	{_T("peachpuff"), 0xffdab9}, 
+	{_T("yellow"), 0xffff00}, 
+	{_T("gold"), 0xffd700}, 
+	{_T("wheat"), 0xf5deb3}, 
+	{_T("orange"), 0xffa500}, 
+	{_T("darkorange"), 0xff8c00}, 
+ 
+	{_T("oldlace"), 0xfdf5e6}, 
+	{_T("linen"), 0xfaf0e6}, 
+	{_T("antiquewhite"), 0xfaebd7}, 
+	{_T("lightsalmon"), 0xffa07a}, 
+	{_T("darksalmon"), 0xe9967a}, 
+	{_T("salmon"), 0xfa8072}, 
+	{_T("lightcoral"), 0xf08080}, 
+	{_T("indianred"), 0xcd5c5c}, 
+	{_T("coral"), 0xff7f50}, 
+	{_T("tomato"), 0xff6347}, 
+	{_T("orangered"), 0xff4500}, 
+	{_T("red"), 0xff0000}, 
+	{_T("crimson"), 0xdc143c}, 
+	{_T("firebrick"), 0xb22222}, 
+	{_T("maroon"), 0x800000}, 
+	{_T("darkred"), 0x8b0000},
+
+	{_T("lavender"), 0xe6e6fe}, 
+	{_T("lavenderblush"), 0xfff0f5}, 
+	{_T("mistyrose"), 0xffe4e1}, 
+	{_T("thistle"), 0xd8bfd8}, 
+	{_T("pink"), 0xffc0cb}, 
+	{_T("lightpink"), 0xffb6c1}, 
+	{_T("palevioletred"), 0xdb7093}, 
+	{_T("hotpink"), 0xff69b4}, 
+	{_T("fuchsia"), 0xff00ee}, 
+	{_T("magenta"), 0xff00ff}, 
+	{_T("mediumvioletred"), 0xc71585}, 
+	{_T("deeppink"), 0xff1493}, 
+	{_T("plum"), 0xdda0dd}, 
+	{_T("violet"), 0xee82ee}, 
+	{_T("orchid"), 0xda70d6}, 
+	{_T("mediumorchid"), 0xba55d3}, 
+	{_T("mediumpurple"), 0x9370db}, 
+	{_T("purple"), 0x9370db}, 
+	{_T("blueviolet"), 0x8a2be2}, 
+	{_T("darkviolet"), 0x9400d3}, 
+	{_T("darkorchid"), 0x9932cc}, 
+ 
+	{_T("tan"), 0xd2b48c}, 
+	{_T("burlywood"), 0xdeb887}, 
+	{_T("sandybrown"), 0xf4a460}, 
+	{_T("peru"), 0xcd853f}, 
+	{_T("goldenrod"), 0xdaa520}, 
+	{_T("darkgoldenrod"), 0xb8860b}, 
+	{_T("chocolate"), 0xd2691e}, 
+	{_T("rosybrown"), 0xbc8f8f}, 
+	{_T("sienna"), 0xa0522d}, 
+	{_T("saddlebrown"), 0x8b4513}, 
+	{_T("brown"), 0xa52a2a}, 
+};
+
+CHtmlColorMap::CHtmlColorMap()
+{
+	for(int i = 0; i < countof(hmtlcolors); i++)
+		SetAt(hmtlcolors[i].name, hmtlcolors[i].color);
+}
+
+CHtmlColorMap g_colors;
+
+//
+
+BYTE CharSetList[] =
+{
+	ANSI_CHARSET,
+	DEFAULT_CHARSET,
+	SYMBOL_CHARSET,
+	SHIFTJIS_CHARSET,
+	HANGEUL_CHARSET,
+	HANGUL_CHARSET,
+	GB2312_CHARSET,
+	CHINESEBIG5_CHARSET,
+	OEM_CHARSET,
+	JOHAB_CHARSET,
+	HEBREW_CHARSET,
+	ARABIC_CHARSET,
+	GREEK_CHARSET,
+	TURKISH_CHARSET,
+	VIETNAMESE_CHARSET,
+	THAI_CHARSET,
+	EASTEUROPE_CHARSET,
+	RUSSIAN_CHARSET,
+	MAC_CHARSET,
+	BALTIC_CHARSET
+};
+
+TCHAR* CharSetNames[] = 
+{
+	_T("ANSI"),
+	_T("DEFAULT"),
+	_T("SYMBOL"),
+	_T("SHIFTJIS"),
+	_T("HANGEUL"),
+	_T("HANGUL"),
+	_T("GB2312"),
+	_T("CHINESEBIG5"),
+	_T("OEM"),
+	_T("JOHAB"),
+	_T("HEBREW"),
+	_T("ARABIC"),
+	_T("GREEK"),
+	_T("TURKISH"),
+	_T("VIETNAMESE"),
+	_T("THAI"),
+	_T("EASTEUROPE"),
+	_T("RUSSIAN"),
+	_T("MAC"),
+	_T("BALTIC"),
+};
+
+int CharSetLen = countof(CharSetList);
+
+//
+
+static DWORD CharSetToCodePage(DWORD dwCharSet)
+{
+	CHARSETINFO cs={0};
+	::TranslateCharsetInfo((DWORD *)dwCharSet, &cs, TCI_SRCCHARSET);
+	return cs.ciACP;
+}
+
+int FindChar(CStringW str, WCHAR c, int pos, bool fUnicode, int CharSet)
+{
+	if(fUnicode) return(str.Find(c, pos));
+
+	int fStyleMod = 0;
+
+	DWORD cp = CharSetToCodePage(CharSet);
+	int OrgCharSet = CharSet;
+
+	for(int i = 0, j = str.GetLength(), k; i < j; i++)
+	{
+		WCHAR c2 = str[i];
+
+		if(IsDBCSLeadByteEx(cp, (BYTE)c2)) i++;
+		else if(i >= pos)
+		{
+			if(c2 == c) return(i);
+		}
+
+		if(c2 == '{') fStyleMod++;
+		else if(fStyleMod > 0)
+		{
+			if(c2 == '}') fStyleMod--;
+			else if(c2 == 'e' && i >= 3 && i < j-1 && str.Mid(i-2, 3) == L"\\fe")
+			{
+				CharSet = 0;
+				for(k = i+1; _istdigit(str[k]); k++) CharSet = CharSet*10 + (str[k] - '0');
+				if(k == i+1) CharSet = OrgCharSet;
+
+				cp = CharSetToCodePage(CharSet);
+			}
+		}
+	}
+
+	return(-1);
+}
+/*
+int FindChar(CStringA str, char c, int pos, bool fUnicode, int CharSet)
+{
+	ASSERT(!fUnicode);
+
+	return(FindChar(AToW(str), c, pos, false, CharSet));
+}
+*/
+static CStringW ToMBCS(CStringW str, DWORD CharSet)
+{
+	CStringW ret;
+
+	DWORD cp = CharSetToCodePage(CharSet);
+
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+	{
+		WCHAR wc = str.GetAt(i);
+		char c[8];
+
+		int len;
+		if((len = WideCharToMultiByte(cp, 0, &wc, 1, c, 8, NULL, NULL)) > 0)
+		{
+			for(int k = 0; k < len; k++)
+				ret += (WCHAR)(BYTE)c[k];
+		}
+		else
+		{
+			ret += '?';
+		}
+	}
+
+	return(ret);
+}
+
+static CStringW UnicodeSSAToMBCS(CStringW str, DWORD CharSet)
+{
+	CStringW ret;
+
+	int OrgCharSet = CharSet;
+
+	for(int j = 0; j < str.GetLength(); )
+	{
+		j = str.Find('{', j);
+		if(j >= 0)
+		{
+			ret += ToMBCS(str.Left(j), CharSet);
+			str = str.Mid(j);
+
+			j = str.Find('}');
+			if(j < 0)
+			{
+				ret += ToMBCS(str, CharSet);
+				break;
+			}
+			else
+			{
+				int k = str.Find(L"\\fe");
+				if(k >= 0 && k < j)
+				{
+					CharSet = 0;
+					int l = k+3;
+					for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
+					if(l == k+3) CharSet = OrgCharSet;
+				}
+
+				j++;
+
+				ret += ToMBCS(str.Left(j), OrgCharSet);
+				str = str.Mid(j);
+				j = 0;
+			}
+		}
+		else
+		{
+			ret += ToMBCS(str, CharSet);
+			break;
+		}
+	}
+
+	return(ret);
+}
+
+static CStringW ToUnicode(CStringW str, DWORD CharSet)
+{
+	CStringW ret;
+
+	DWORD cp = CharSetToCodePage(CharSet);
+
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+	{
+		WCHAR wc = str.GetAt(i);
+		char c = wc&0xff;
+
+		if(IsDBCSLeadByteEx(cp, (BYTE)wc))
+		{
+			i++;
+
+			if(i < j)
+			{
+				char cc[2];
+				cc[0] = c;
+				cc[1] = (char)str.GetAt(i);
+
+				MultiByteToWideChar(cp, 0, cc, 2, &wc, 1);
+			}
+		}
+		else
+		{
+			MultiByteToWideChar(cp, 0, &c, 1, &wc, 1);
+		}
+
+		ret += wc;
+	}
+
+	return(ret);
+}
+
+static CStringW MBCSSSAToUnicode(CStringW str, int CharSet)
+{
+	CStringW ret;
+
+	int OrgCharSet = CharSet;
+
+	for(int j = 0; j < str.GetLength(); )
+	{
+		j = FindChar(str, '{', 0, false, CharSet);
+
+		if(j >= 0)
+		{
+			ret += ToUnicode(str.Left(j), CharSet);
+			str = str.Mid(j);
+
+			j = FindChar(str, '}', 0, false, CharSet);
+
+			if(j < 0)
+			{
+				ret += ToUnicode(str, CharSet);
+				break;
+			}
+			else
+			{
+				int k = str.Find(L"\\fe");
+				if(k >= 0 && k < j)
+				{
+					CharSet = 0;
+					int l = k+3;
+					for(; _istdigit(str[l]); l++) CharSet = CharSet*10 + (str[l] - '0');
+					if(l == k+3) CharSet = OrgCharSet;
+				}
+
+				j++;
+
+				ret += ToUnicode(str.Left(j), OrgCharSet);
+				str = str.Mid(j);
+				j = 0;
+			}
+		}
+		else
+		{
+			ret += ToUnicode(str, CharSet);
+			break;
+		}
+	}
+
+	return(ret);
+}
+
+CStringW RemoveSSATags(CStringW str, bool fUnicode, int CharSet)
+{
+	for(int i = 0, j; i < str.GetLength(); )
+	{
+		if((i = FindChar(str, '{', i, fUnicode, CharSet)) < 0) break;
+		if((j = FindChar(str, '}', i, fUnicode, CharSet)) < 0) break;
+		str.Delete(i, j-i+1);
+	}
+
+	str.Replace(L"\\N", L"\n");
+	str.Replace(L"\\n", L"\n");
+	str.Replace(L"\\h", L" ");
+
+	return(str);
+}
+
+//
+
+static CStringW SubRipper2SSA(CStringW str, int CharSet)
+{
+	str.Replace(L"<i>", L"{\\i1}");
+	str.Replace(L"</i>", L"{\\i}");
+	str.Replace(L"<b>", L"{\\b1}");
+	str.Replace(L"</b>", L"{\\b}");
+	str.Replace(L"<u>", L"{\\u1}");
+	str.Replace(L"</u>", L"{\\u}");
+
+	return(str);
+}
+
+static bool OpenSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	int num = 0;
+
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+		
+		WCHAR sep;
+		int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2;
+		int c = swscanf(buff, L"%d%c%d%c%d%c%d --> %d%c%d%c%d%c%d\n", 
+			&hh1, &sep, &mm1, &sep, &ss1, &sep, &ms1,
+			&hh2, &sep, &mm2, &sep, &ss2, &sep, &ms2);
+
+		if(c == 1) // numbering
+		{
+			num = hh1;
+		}
+		else if(c == 14) // time info
+		{
+			CStringW str, tmp;
+
+			bool fFoundEmpty = false;
+
+			while(file->ReadString(tmp))
+			{
+				tmp.Trim();
+				if(tmp.IsEmpty()) fFoundEmpty = true;
+
+				int num2;
+				WCHAR c;
+				if(swscanf(tmp, L"%d%c", &num2, &c) == 1 && fFoundEmpty)
+				{
+					num = num2;
+					break;
+				}
+
+				str += tmp + '\n';
+			}
+
+			ret.Add(
+				SubRipper2SSA(str, CharSet), 
+				file->IsUnicode(),
+				(((hh1*60 + mm1)*60) + ss1)*1000 + ms1, 
+				(((hh2*60 + mm2)*60) + ss2)*1000 + ms2);
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+static bool OpenOldSubRipper(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+
+		for(int i = 0; i < buff.GetLength(); i++)
+		{
+			if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
+			buff.SetAt(i, '\n');
+		}
+
+		int hh1, mm1, ss1, hh2, mm2, ss2;
+		int c = swscanf(buff, L"{%d:%d:%d}{%d:%d:%d}", &hh1, &mm1, &ss1, &hh2, &mm2, &ss2);
+
+		if(c == 6)
+		{
+			ret.Add(
+				buff.Mid(buff.Find('}', buff.Find('}')+1)+1), 
+				file->IsUnicode(),
+				(((hh1*60 + mm1)*60) + ss1)*1000, 
+				(((hh2*60 + mm2)*60) + ss2)*1000);
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+static bool OpenSubViewer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	STSStyle def;
+	CStringW font, color, size;
+	bool fBold, fItalic, fStriked, fUnderline;
+
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+		
+		if(buff[0] == '[')
+		{
+			for(int i = 0; i < buff.GetLength() && buff[i]== '['; )
+			{
+				int j = buff.Find(']', ++i);
+				if(j < i) break;
+
+				CStringW tag = buff.Mid(i,j-i);
+				tag.Trim();
+				tag.MakeLower();
+
+				i += j-i;
+
+				j = buff.Find('[', ++i);
+				if(j < 0) j = buff.GetLength();
+
+				CStringW param = buff.Mid(i,j-i);
+				param.Trim(L" \\t,");
+
+				i = j;
+
+				if(tag == L"font")
+					font = def.fontName.CompareNoCase(WToT(param)) ? param : L"";
+				else if(tag == L"colf")
+					color = def.colors[0] != wcstol(((LPCWSTR)param)+2, 0, 16) ? param : L"";
+				else if(tag == L"size")
+					size = def.fontSize != wcstol(param, 0, 10) ? param : L"";
+				else if(tag == L"style")
+				{
+					if(param.Find(L"no") >= 0)
+					{
+						fBold = fItalic = fStriked = fUnderline = false;
+					}
+					else
+					{
+						fBold = def.fontWeight < FW_BOLD && param.Find(L"bd") >= 0;
+						fItalic = def.fItalic && param.Find(L"it") >= 0;
+						fStriked = def.fStrikeOut && param.Find(L"st") >= 0;
+						fUnderline = def.fUnderline && param.Find(L"ud") >= 0;
+					}
+				}
+			}
+
+			continue;
+		}
+
+		WCHAR sep;
+		int hh1, mm1, ss1, hs1, hh2, mm2, ss2, hs2;
+		int c = swscanf(buff, L"%d:%d:%d%c%d,%d:%d:%d%c%d\n", 
+			&hh1, &mm1, &ss1, &sep, &hs1, &hh2, &mm2, &ss2, &sep, &hs2);
+
+		if(c == 10)
+		{
+			CStringW str;
+			file->ReadString(str);
+
+			str.Replace(L"[br]", L"\\N");
+
+			CStringW prefix;
+			if(!font.IsEmpty()) prefix += L"\\fn" + font;
+			if(!color.IsEmpty()) prefix += L"\\c" + color;
+			if(!size.IsEmpty()) prefix += L"\\fs" + size;
+			if(fBold) prefix += L"\\b1";
+			if(fItalic) prefix += L"\\i1";
+			if(fStriked) prefix += L"\\s1";
+			if(fUnderline) prefix += L"\\u1";
+			if(!prefix.IsEmpty()) str = L"{" + prefix + L"}" + str;
+
+			ret.Add(str,
+				file->IsUnicode(),
+				(((hh1*60 + mm1)*60) + ss1)*1000 + hs1*10,
+				(((hh2*60 + mm2)*60) + ss2)*1000 + hs2*10);
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+static STSStyle* GetMicroDVDStyle(CString str, int CharSet)
+{
+	STSStyle* ret = new STSStyle();
+	if(!ret) return(NULL);
+
+	for(int i = 0, len = str.GetLength(); i < len; i++)
+	{
+		int j = str.Find('{', i);
+		if(j < 0) j = len;
+
+		if(j >= len) break;
+
+		int k = str.Find('}', j);
+		if(k < 0) k = len;
+
+		CString code = str.Mid(j, k-j);
+		if(code.GetLength() > 2) code.SetAt(1, (TCHAR)towlower(code[1]));
+
+		if(!_tcsnicmp(code, _T("{c:$"), 4))
+		{
+			_stscanf(code, _T("{c:$%x"), &ret->colors[0]);
+		}
+		else if(!_tcsnicmp(code, _T("{f:"), 3))
+		{
+			ret->fontName = code.Mid(3);
+		}
+		else if(!_tcsnicmp(code, _T("{s:"), 3))
+		{
+			float f;
+			if(1 == _stscanf(code, _T("{s:%f"), &f))
+				ret->fontSize = f;
+		}
+		else if(!_tcsnicmp(code, _T("{h:"), 3))
+		{
+			_stscanf(code, _T("{h:%d"), &ret->charSet);
+		}
+		else if(!_tcsnicmp(code, _T("{y:"), 3))
+		{
+			code.MakeLower();
+			if(code.Find('b') >= 0) ret->fontWeight = FW_BOLD;
+			if(code.Find('i') >= 0) ret->fItalic = true;
+			if(code.Find('u') >= 0) ret->fUnderline = true;
+			if(code.Find('s') >= 0) ret->fStrikeOut = true;
+		}
+		else if(!_tcsnicmp(code, _T("{p:"), 3))
+		{
+			int p;
+			_stscanf(code, _T("{p:%d"), &p);
+			ret->scrAlignment = (p == 0) ? 8 : 2;
+		}
+
+		i = k;
+	}
+
+	return(ret);
+}
+
+static CStringW MicroDVD2SSA(CStringW str, bool fUnicode, int CharSet)
+{
+	CStringW ret;
+
+	enum {COLOR=0, FONTNAME, FONTSIZE, FONTCHARSET, BOLD, ITALIC, UNDERLINE, STRIKEOUT};
+	bool fRestore[8];
+	int fRestoreLen = 8;
+	memset(fRestore, 0, sizeof(bool)*fRestoreLen);
+
+	for(int pos = 0, eol; pos < str.GetLength(); pos++)
+	{
+		if((eol = FindChar(str, '|', pos, fUnicode, CharSet)) < 0) eol = str.GetLength();
+
+		CStringW line = str.Mid(pos, eol-pos);
+
+		pos = eol;
+
+		for(int i = 0, j, k, len = line.GetLength(); i < len; i++)
+		{
+			if((j = FindChar(line, '{', i, fUnicode, CharSet)) < 0) j = str.GetLength();
+
+			ret += line.Mid(i, j-i);
+
+			if(j >= len) break;
+
+			if((k = FindChar(line, '}', j, fUnicode, CharSet)) < 0) k = len;
+
+			{
+				CStringW code = line.Mid(j, k-j);
+
+				if(!wcsnicmp(code, L"{c:$", 4))
+				{
+					fRestore[COLOR] = (iswupper(code[1]) == 0);
+					code.MakeLower();
+
+					int color;
+					swscanf(code, L"{c:$%x", &color);
+					code.Format(L"{\\c&H%x&}", color);
+					ret += code;
+				}
+				else if(!wcsnicmp(code, L"{f:", 3))
+				{
+					fRestore[FONTNAME] = (iswupper(code[1]) == 0);
+
+					code.Format(L"{\\fn%s}", code.Mid(3));
+					ret += code;
+				}
+				else if(!wcsnicmp(code, L"{s:", 3))
+				{
+					fRestore[FONTSIZE] = (iswupper(code[1]) == 0);
+					code.MakeLower();
+
+					float size;
+					swscanf(code, L"{s:%f", &size);
+					code.Format(L"{\\fs%f}", size);
+					ret += code;
+				}
+				else if(!wcsnicmp(code, L"{h:", 3))
+				{
+					fRestore[COLOR] = (_istupper(code[1]) == 0);
+					code.MakeLower();
+
+					int CharSet;
+					swscanf(code, L"{h:%d", &CharSet);
+					code.Format(L"{\\fe%d}", CharSet);
+					ret += code;
+				}
+				else if(!wcsnicmp(code, L"{y:", 3))
+				{
+					bool f = (_istupper(code[1]) == 0);
+
+					code.MakeLower();
+
+					ret += '{';
+					if(code.Find('b') >= 0) {ret += L"\\b1"; fRestore[BOLD] = f;}
+					if(code.Find('i') >= 0) {ret += L"\\i1"; fRestore[ITALIC] = f;}
+					if(code.Find('u') >= 0) {ret += L"\\u1"; fRestore[UNDERLINE] = f;}
+					if(code.Find('s') >= 0) {ret += L"\\s1"; fRestore[STRIKEOUT] = f;}
+					ret += '}';
+				}
+				else if(!wcsnicmp(code, L"{o:", 3))
+				{
+					code.MakeLower();
+
+					int x, y;
+					TCHAR c;
+					swscanf(code, L"{o:%d%c%d", &x, &c, &y);
+					code.Format(L"{\\move(%d,%d,0,0,0,0)}", x, y);
+					ret += code;
+				}
+				else ret += code;
+			}
+
+			i = k;
+		}
+
+		if(pos >= str.GetLength()) break;
+
+		for(int i = 0; i < fRestoreLen; i++)
+		{
+			if(fRestore[i]) 
+			{
+				switch(i)
+				{
+				case COLOR: ret += L"{\\c}"; break;
+				case FONTNAME: ret += L"{\\fn}"; break;
+				case FONTSIZE: ret += L"{\\fs}"; break;
+				case FONTCHARSET: ret += L"{\\fe}"; break;
+				case BOLD: ret += L"{\\b}"; break;
+				case ITALIC: ret += L"{\\i}"; break;
+				case UNDERLINE: ret += L"{\\u}"; break;
+				case STRIKEOUT: ret += L"{\\s}"; break;
+				default: break;
+				}
+			}
+		}
+
+		memset(fRestore, 0, sizeof(bool)*fRestoreLen);
+
+		ret += L"\\N";
+	}
+
+	return(ret);
+}
+
+static bool OpenMicroDVD(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	bool fCheck = false, fCheck2 = false;
+
+	CString style(_T("Default"));
+
+	CStringW buff;;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+
+		int start, end;
+		int c = swscanf(buff, L"{%d}{%d}", &start, &end);
+
+		if(c != 2) {c = swscanf(buff, L"{%d}{}", &start)+1; end = start + 60; fCheck = true;}
+
+		if(c != 2)
+		{
+			int i;
+			if(buff.Find('{') == 0 && (i = buff.Find('}')) > 1 && i < buff.GetLength())
+			{
+				if(STSStyle* s = GetMicroDVDStyle(WToT(buff.Mid(i+1)), CharSet))
+				{
+					style = buff.Mid(1, i-1);
+					style.MakeUpper();
+					if(style.GetLength()) {CString str = style.Mid(1); str.MakeLower(); style = style.Left(1) + str;}
+					ret.AddStyle(style, s);
+					CharSet = s->charSet;
+					continue;
+				}
+			}
+		}
+
+		if(c == 2)
+		{
+			if(fCheck2 && ret.GetCount())
+			{
+				STSEntry& stse = ret[ret.GetCount()-1];
+				stse.end = min(stse.end, start);
+				fCheck2 = false;
+			}
+
+			ret.Add(
+				MicroDVD2SSA(buff.Mid(buff.Find('}', buff.Find('}')+1)+1), file->IsUnicode(), CharSet), 
+				file->IsUnicode(),
+				start, end, 
+				style);
+
+			if(fCheck) 
+			{
+				fCheck = false;
+				fCheck2 = true;
+			}
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+static void ReplaceNoCase(CStringW& str, CStringW from, CStringW to)
+{
+	CStringW lstr = str;
+	lstr.MakeLower();
+
+	int i, j, k;
+
+	for(i = 0, j = str.GetLength(); i < j; )
+	{
+		if((k = lstr.Find(from, i)) >= 0) 
+		{
+			str.Delete(k, from.GetLength()); lstr.Delete(k, from.GetLength());
+			str.Insert(k, to); lstr.Insert(k, to);
+			i = k + to.GetLength();
+			j = str.GetLength();
+		}
+		else break;
+	}
+}
+
+static CStringW SMI2SSA(CStringW str, int CharSet)
+{
+	ReplaceNoCase(str, L"&nbsp;", L" ");
+	ReplaceNoCase(str, L"&quot;", L"\"");
+	ReplaceNoCase(str, L"<br>", L"\\N");
+	ReplaceNoCase(str, L"<i>", L"{\\i1}");
+	ReplaceNoCase(str, L"</i>", L"{\\i}");
+	ReplaceNoCase(str, L"<b>", L"{\\b1}");
+	ReplaceNoCase(str, L"</b>", L"{\\b}");
+
+	CStringW lstr = str;
+	lstr.MakeLower();
+
+	// maven@maven.de
+	// now parse line
+	for(int i = 0, j = str.GetLength(); i < j; )
+	{
+		int k;
+		if((k = lstr.Find('<', i)) < 0) break;
+
+		int chars_inserted = 0;
+
+		int l = 1;
+		for(; k+l < j && lstr[k+l] != '>'; l++);
+		l++;
+
+// Modified by Cookie Monster 
+		if (lstr.Find(L"<font ", k) == k)
+		{
+			CStringW args = lstr.Mid(k+6, l-6);	// delete "<font "
+			CStringW arg ;
+
+			args.Remove('\"'); args.Remove('#');	// may include 2 * " + #
+			arg.TrimLeft(); arg.TrimRight(L" >");
+
+			for (;;)
+			{
+				args.TrimLeft();
+				arg = args.SpanExcluding(L" \t>");
+				args = args.Mid(arg.GetLength());
+
+				if(arg.IsEmpty())
+					break;
+				if (arg.Find(L"color=") == 0 )
+				{
+					DWORD color;
+
+					arg = arg.Mid(6);	// delete "color="
+					if ( arg.IsEmpty())
+						continue;
+
+					DWORD val;
+					if(g_colors.Lookup(CString(arg), val))
+						color = (DWORD)val;
+					else if((color = wcstol(arg, NULL, 16) ) == 0)
+						color = 0x00ffffff;  // default is white
+
+					arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
+					lstr.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
+					str.Insert(k + l + chars_inserted, CStringW(L"{\\c&H") + arg + L"&}");
+					chars_inserted += 5 + arg.GetLength() + 2;
+				}
+/*
+				else if (arg.Find(_T("size=" )) == 0 )
+				{
+					uint fsize;
+
+					arg = arg.Mid(5);	// delete "size="
+					if ( arg.GetLength() == 0)
+						continue;
+
+					if ( fsize = _tcstol(arg, &tmp, 10) == 0 )
+						continue;
+					
+					lstr.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
+					str.Insert(k + l + chars_inserted, CString(_T("{\\fs")) + arg + _T("&}"));
+					chars_inserted += 4 + arg.GetLength() + 2;
+				}
+*/
+			}
+		}
+
+// Original Code
+/*
+		if (lstr.Find(L"<font color=", k) == k)
+		{
+			CStringW arg = lstr.Mid(k+12, l-12); // may include 2 * " + #
+
+			arg.Remove('\"');
+			arg.Remove('#');
+			arg.TrimLeft(); arg.TrimRight(L" >");
+
+			if(arg.GetLength() > 0)
+			{
+				DWORD color;
+				
+				CString key = WToT(arg);
+				void* val;
+				if(g_colors.Lookup(key, val)) color = (DWORD)val;
+				else color = wcstol(arg, NULL, 16);
+
+				arg.Format(L"%02x%02x%02x", color&0xff, (color>>8)&0xff, (color>>16)&0xff);
+			}
+			
+			lstr.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
+			str.Insert(k + l + chars_inserted, L"{\\c&H" + arg + L"&}");
+			chars_inserted += 5 + arg.GetLength() + 2;
+		}
+*/
+		else if (lstr.Find(L"</font>", k) == k)
+		{
+			lstr.Insert(k + l + chars_inserted, L"{\\c}");
+			str.Insert(k + l + chars_inserted, L"{\\c}");
+			chars_inserted += 4;
+		}
+		
+		str.Delete(k, l); lstr.Delete(k, l);
+		i = k + chars_inserted;
+		j = str.GetLength();
+	}
+
+	return(str);
+}
+
+static bool OpenSami(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	CStringW buff, caption;
+
+	ULONGLONG pos = file->GetPosition();
+
+	bool fSAMI = false;
+
+	while(file->ReadString(buff) && !fSAMI)
+	{
+		if(buff.MakeUpper().Find(L"<SAMI>") >= 0) fSAMI = true;
+	}
+
+	if(!fSAMI) return(false);
+
+	file->Seek(pos, 0);
+
+	bool fComment = false;
+
+	int start_time = 0;
+
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+
+		CStringW ubuff = buff;
+		ubuff.MakeUpper();
+
+		if(ubuff.Find(L"<!--") >= 0 || ubuff.Find(L"<TITLE>") >= 0)
+			fComment = true;
+
+		if(!fComment)
+		{
+			int i;
+
+			if((i = ubuff.Find(L"<SYNC START=")) >= 0)
+			{
+				int time = 0;
+
+				for(i = 12; i < ubuff.GetLength(); i++)
+				{
+					if(ubuff[i] != '>' && ubuff[i] != 'M')
+					{
+						if(iswdigit(ubuff[i]))
+						{
+							time *= 10;
+							time += ubuff[i] - 0x30;
+						}
+					}
+					else break;
+				}
+
+				ret.Add(
+					SMI2SSA(caption, CharSet), 
+					file->IsUnicode(),
+					start_time, time);
+
+				start_time = time;
+				caption.Empty();
+			}
+
+			caption += buff;
+		}
+
+		if(ubuff.Find(L"-->") >= 0 || ubuff.Find(L"</TITLE>") >= 0)
+			fComment = false;
+	}
+
+	ret.Add(
+		SMI2SSA(caption, CharSet), 
+		file->IsUnicode(),
+		start_time, MAXLONG);
+
+	return(true);
+}
+
+static bool OpenVPlayer(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+
+		for(int i = 0; i < buff.GetLength(); i++)
+		{
+			if((i = FindChar(buff, '|', i, file->IsUnicode(), CharSet)) < 0) break;
+			buff.SetAt(i, '\n');
+		}
+
+		int hh, mm, ss;
+		int c = swscanf(buff, L"%d:%d:%d:", &hh, &mm, &ss);
+
+		if(c == 3)
+		{
+			CStringW str = buff.Mid(buff.Find(':', buff.Find(':', buff.Find(':')+1)+1)+1);
+			ret.Add(str, 
+				file->IsUnicode(),
+				(((hh*60 + mm)*60) + ss)*1000, 
+				(((hh*60 + mm)*60) + ss)*1000 + 1000 + 50*str.GetLength());
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+CStringW GetStr(CStringW& buff, char sep = ',') //throw(...)
+{
+	buff.TrimLeft();
+
+	int pos = buff.Find(sep);
+	if(pos < 0) 
+	{
+		pos = buff.GetLength();
+		if(pos < 1) throw 1;
+	}
+
+	CStringW ret = buff.Left(pos);
+	if(pos < buff.GetLength()) buff = buff.Mid(pos+1);
+
+	return(ret);
+}
+
+int GetInt(CStringW& buff, char sep = ',') //throw(...) 
+{
+	CStringW str;
+
+	str = GetStr(buff, sep);
+	str.MakeLower();
+
+	CStringW fmtstr = str.GetLength() > 2 && (str.Left(2) == L"&h" || str.Left(2) == L"0x")
+		? str = str.Mid(2), L"%x"
+		: L"%d";
+
+	int ret;
+	if(swscanf(str, fmtstr, &ret) != 1) throw 1;
+
+	return(ret);
+}
+
+double GetFloat(CStringW& buff, char sep = ',') //throw(...) 
+{
+	CStringW str;
+
+	str = GetStr(buff, sep);
+	str.MakeLower();
+
+	float ret;
+	if(swscanf(str, L"%f", &ret) != 1) throw 1;
+
+	return((double)ret);
+}
+
+static bool LoadFont(CString& font)
+{
+	int len = font.GetLength();
+
+	CAutoVectorPtr<BYTE> pData;
+	if(len == 0 || (len&3) == 1 || !pData.Allocate(len))
+		return(false);
+
+	const TCHAR* s = font;
+	const TCHAR* e = s + len;
+	for(BYTE* p = pData; s < e; s++, p++) *p = *s - 33;
+
+	for(int i = 0, j = 0, k = len&~3; i < k; i+=4, j+=3)
+	{
+		pData[j+0] = ((pData[i+0]&63)<<2)|((pData[i+1]>>4)& 3);
+		pData[j+1] = ((pData[i+1]&15)<<4)|((pData[i+2]>>2)&15);
+		pData[j+2] = ((pData[i+2]& 3)<<6)|((pData[i+3]>>0)&63);
+	}
+
+	int datalen = (len&~3)*3/4;
+
+	if((len&3) == 2)
+	{
+		pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)&3);
+	}
+	else if((len&3) == 3)
+	{
+		pData[datalen++] = ((pData[(len&~3)+0]&63)<<2)|((pData[(len&~3)+1]>>4)& 3);
+		pData[datalen++] = ((pData[(len&~3)+1]&15)<<4)|((pData[(len&~3)+2]>>2)&15);
+	}
+
+	HANDLE hFont = INVALID_HANDLE_VALUE;
+
+	if(HMODULE hModule = LoadLibrary(_T("GDI32.DLL")))
+	{
+		typedef HANDLE (WINAPI *PAddFontMemResourceEx)( IN PVOID, IN DWORD, IN PVOID , IN DWORD*);
+		if(PAddFontMemResourceEx f = (PAddFontMemResourceEx)GetProcAddress(hModule, "AddFontMemResourceEx"))
+		{
+			DWORD cFonts;
+			hFont = f(pData, datalen, NULL, &cFonts);
+		}
+
+		FreeLibrary(hModule);
+	}
+
+	if(hFont == INVALID_HANDLE_VALUE)
+	{
+		TCHAR path[MAX_PATH];
+		GetTempPath(MAX_PATH, path);
+
+		DWORD chksum = 0;
+		for(int i = 0, j = datalen>>2; i < j; i++)
+			chksum += ((DWORD*)(BYTE*)pData)[i];
+
+		CString fn;
+		fn.Format(_T("%sfont%08x.ttf"), path, chksum);
+
+		CFileStatus	fs;
+		if(!CFileGetStatus(fn, fs))
+		{
+			CFile f;
+			if(f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
+			{
+				f.Write(pData, datalen);
+				f.Close();
+			}
+		}
+
+		AddFontResource(fn);
+	}
+
+	return(true);
+}
+
+static bool LoadUUEFont(CTextFile* file)
+{
+	CString s, font;
+	while(file->ReadString(s))
+	{
+		s.Trim();
+		if(s.IsEmpty() || s[0] == '[') break;
+		if(s.Find(_T("fontname:")) == 0) {LoadFont(font); font.Empty(); continue;}
+
+		font += s;
+	}
+
+	if(!font.IsEmpty())
+		LoadFont(font);
+
+	return(true);
+}
+
+static bool OpenSubStationAlpha(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	bool fRet = false;
+
+	int version = 3, sver = 3;
+
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
+
+		CStringW entry;
+
+//		try {
+			entry = GetStr(buff, ':');
+//	}
+//		catch(...) {continue;}
+		
+		entry.MakeLower();
+
+		if(entry == L"[script info]")
+		{
+			fRet = true;
+		}
+		else if(entry == L"playresx")
+		{
+			try {ret.m_dstScreenSize.cx = GetInt(buff);}
+			catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
+
+			if(ret.m_dstScreenSize.cy <= 0)
+			{
+				ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
+					? 1024 
+					: ret.m_dstScreenSize.cx * 3 / 4;
+			}
+		}
+		else if(entry == L"playresy")
+		{
+			try {ret.m_dstScreenSize.cy = GetInt(buff);}
+			catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
+
+			if(ret.m_dstScreenSize.cx <= 0)
+			{
+				ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
+					? 1280 
+					: ret.m_dstScreenSize.cy * 4 / 3;
+			}
+		}
+		else if(entry == L"wrapstyle")
+		{
+			try {ret.m_defaultWrapStyle = GetInt(buff);}
+			catch(...) {ret.m_defaultWrapStyle = 1; return(false);}
+		}
+		else if(entry == L"scripttype")
+		{
+			if(buff.GetLength() >= 4 && !buff.Right(4).CompareNoCase(L"4.00")) version = sver = 4;
+			else if(buff.GetLength() >= 5 && !buff.Right(5).CompareNoCase(L"4.00+")) version = sver = 5;
+			else if(buff.GetLength() >= 6 && !buff.Right(6).CompareNoCase(L"4.00++")) version = sver = 6;
+		}
+		else if(entry == L"collisions")
+		{
+			buff = GetStr(buff);
+			buff.MakeLower();
+			ret.m_collisions = buff.Find(L"reverse") >= 0 ? 1 : 0;
+		}
+		else if(entry == L"scaledborderandshadow")
+		{
+			buff = GetStr(buff);
+			buff.MakeLower();
+			ret.m_fScaledBAS = buff.Find(L"yes") >= 0;
+		}
+		else if(entry == L"[v4 styles]")
+		{
+			fRet = true;
+            sver = 4;
+		}
+		else if(entry == L"[v4+ styles]")
+		{
+			fRet = true;
+            sver = 5;
+		}
+		else if(entry == L"[v4++ styles]")
+		{
+			fRet = true;
+            sver = 6;
+		}
+		else if(entry == L"style")
+		{
+			STSStyle* style = new STSStyle;
+			if(!style) return(false);
+
+			try 
+			{
+				CString StyleName;
+				int alpha;
+
+				StyleName = WToT(GetStr(buff));
+				style->fontName = WToT(GetStr(buff));
+				style->fontSize = GetFloat(buff);
+				for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
+				style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
+				style->fItalic = !!GetInt(buff);
+if(sver >= 5)	style->fUnderline = !!GetInt(buff);
+if(sver >= 5)	style->fStrikeOut = !!GetInt(buff);
+if(sver >= 5)	style->fontScaleX = GetFloat(buff);
+if(sver >= 5)	style->fontScaleY = GetFloat(buff);
+if(sver >= 5)	style->fontSpacing = GetFloat(buff);
+if(sver >= 5)	style->fontAngleZ = GetFloat(buff);
+if(sver >= 4)	style->borderStyle = GetInt(buff);
+				style->outlineWidth = GetFloat(buff);
+				style->shadowDepth = GetFloat(buff);
+				style->scrAlignment = GetInt(buff);
+				style->marginRect.left = GetInt(buff);
+				style->marginRect.right = GetInt(buff);
+				style->marginRect.top = style->marginRect.bottom = GetInt(buff);
+if(sver >= 6)	style->marginRect.bottom = GetInt(buff);
+if(sver <= 4)	alpha = GetInt(buff);
+				style->charSet = GetInt(buff);
+if(sver >= 6)	style->relativeTo = GetInt(buff);
+
+if(sver <= 4)	style->colors[2] = style->colors[3]; // style->colors[2] is used for drawing the outline
+if(sver <= 4)	alpha = max(min(alpha, 0xff), 0);
+if(sver <= 4)	{for(int i = 0; i < 3; i++) style->alpha[i] = alpha; style->alpha[3] = 0x80;}
+if(sver >= 5)	for(int i = 0; i < 4; i++) {style->alpha[i] = (BYTE)(style->colors[i]>>24); style->colors[i] &= 0xffffff;}
+if(sver >= 5)	style->fontScaleX = max(style->fontScaleX, 0);
+if(sver >= 5)	style->fontScaleY = max(style->fontScaleY, 0);
+if(sver >= 5)	style->fontSpacing = max(style->fontSpacing, 0);
+				style->fontAngleX = style->fontAngleY = 0;
+				style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
+				style->outlineWidth = max(style->outlineWidth, 0);
+				style->shadowDepth = max(style->shadowDepth, 0);
+if(sver <= 4)	style->scrAlignment = (style->scrAlignment&4) ? ((style->scrAlignment&3)+6) // top
+										: (style->scrAlignment&8) ? ((style->scrAlignment&3)+3) // mid
+										: (style->scrAlignment&3); // bottom
+				
+				StyleName.TrimLeft('*');
+
+				ret.AddStyle(StyleName, style);
+			}
+			catch(...)
+			{
+				delete style;
+				return(false);
+			}
+		}
+		else if(entry == L"[events]")
+		{
+			fRet = true;
+		}
+		else if(entry == _T("dialogue"))
+		{
+			try
+			{
+				int hh1, mm1, ss1, ms1_div10, hh2, mm2, ss2, ms2_div10, layer = 0;
+				CString Style, Actor, Effect;
+				CRect marginRect;
+
+if(version <= 4){GetStr(buff, '='); GetInt(buff);} /* Marked = */
+if(version >= 5)layer = GetInt(buff);
+				hh1 = GetInt(buff, ':');
+				mm1 = GetInt(buff, ':');
+				ss1 = GetInt(buff, '.');
+				ms1_div10 = GetInt(buff);
+				hh2 = GetInt(buff, ':');
+				mm2 = GetInt(buff, ':');
+				ss2 = GetInt(buff, '.');
+				ms2_div10 = GetInt(buff);
+				Style = WToT(GetStr(buff));
+				Actor = WToT(GetStr(buff));
+				marginRect.left = GetInt(buff);
+				marginRect.right = GetInt(buff);
+				marginRect.top = marginRect.bottom = GetInt(buff);
+if(version >= 6)marginRect.bottom = GetInt(buff);
+				Effect = WToT(GetStr(buff));
+
+				int len = min(Effect.GetLength(), buff.GetLength());
+				if(Effect.Left(len) == WToT(buff.Left(len))) Effect.Empty();
+
+				Style.TrimLeft('*');
+				if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
+
+				ret.Add(buff,
+					file->IsUnicode(),
+					(((hh1*60 + mm1)*60) + ss1)*1000 + ms1_div10*10, 
+					(((hh2*60 + mm2)*60) + ss2)*1000 + ms2_div10*10, 
+					Style, Actor, Effect,
+					marginRect,
+					layer);
+			}
+			catch(...)
+			{
+				return(false);
+			}
+		}
+		else if(entry == L"fontname")
+		{
+			LoadUUEFont(file);
+		}
+	}
+
+	return(fRet);
+}
+
+static bool OpenXombieSub(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	float version = 0;
+
+//	CMapStringToPtr stylemap;
+
+	CStringW buff;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty() || buff.GetAt(0) == ';') continue;
+
+		CStringW entry;
+
+//		try {
+			entry = GetStr(buff, '=');
+//	}
+//		catch(...) {continue;}
+		
+		entry.MakeLower();
+
+		if(entry == L"version")
+		{
+            version = (float)GetFloat(buff);
+		}
+		else if(entry == L"screenhorizontal")
+		{
+			try {ret.m_dstScreenSize.cx = GetInt(buff);}
+			catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
+
+			if(ret.m_dstScreenSize.cy <= 0)
+			{
+				ret.m_dstScreenSize.cy = (ret.m_dstScreenSize.cx == 1280)
+					? 1024 
+					: ret.m_dstScreenSize.cx * 3 / 4;
+			}
+		}
+		else if(entry == L"screenvertical")
+		{
+			try {ret.m_dstScreenSize.cy = GetInt(buff);}
+			catch(...) {ret.m_dstScreenSize = CSize(0, 0); return(false);}
+
+			if(ret.m_dstScreenSize.cx <= 0)
+			{
+				ret.m_dstScreenSize.cx = (ret.m_dstScreenSize.cy == 1024)
+					? 1280 
+					: ret.m_dstScreenSize.cy * 4 / 3;
+			}
+		}
+		else if(entry == L"style")
+		{
+			STSStyle* style = new STSStyle;
+			if(!style) return(false);
+
+			try 
+			{
+				CString StyleName;
+
+				StyleName = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
+				style->fontName = WToT(GetStr(buff));
+				style->fontSize = GetFloat(buff);
+				for(int i = 0; i < 4; i++) style->colors[i] = (COLORREF)GetInt(buff);
+				for(int i = 0; i < 4; i++) style->alpha[i] = GetInt(buff);
+				style->fontWeight = !!GetInt(buff) ? FW_BOLD : FW_NORMAL;
+				style->fItalic = !!GetInt(buff);
+				style->fUnderline = !!GetInt(buff);
+				style->fStrikeOut = !!GetInt(buff);
+				style->fBlur = !!GetInt(buff);
+				style->fontScaleX = GetFloat(buff);
+				style->fontScaleY = GetFloat(buff);
+				style->fontSpacing = GetFloat(buff);
+				style->fontAngleX = GetFloat(buff);
+				style->fontAngleY = GetFloat(buff);
+				style->fontAngleZ = GetFloat(buff);
+				style->borderStyle = GetInt(buff);
+				style->outlineWidth = GetFloat(buff);
+				style->shadowDepth = GetFloat(buff);
+				style->scrAlignment = GetInt(buff);
+				style->marginRect.left = GetInt(buff);
+				style->marginRect.right = GetInt(buff);
+				style->marginRect.top = style->marginRect.bottom = GetInt(buff);
+				style->charSet = GetInt(buff);
+
+				style->fontScaleX = max(style->fontScaleX, 0);
+				style->fontScaleY = max(style->fontScaleY, 0);
+				style->fontSpacing = max(style->fontSpacing, 0);
+				style->borderStyle = style->borderStyle == 1 ? 0 : style->borderStyle == 3 ? 1 : 0;
+				style->outlineWidth = max(style->outlineWidth, 0);
+				style->shadowDepth = max(style->shadowDepth, 0);
+				
+				ret.AddStyle(StyleName, style);
+			}
+			catch(...)
+			{
+				delete style;
+				return(false);
+			}
+		}
+		else if(entry == L"line")
+		{
+			try
+			{
+				CString id;
+				int hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, layer = 0;
+				CString Style, Actor;
+				CRect marginRect;
+
+				if(GetStr(buff) != L"D") continue;
+				id = GetStr(buff);
+				layer = GetInt(buff);
+				hh1 = GetInt(buff, ':');
+				mm1 = GetInt(buff, ':');
+				ss1 = GetInt(buff, '.');
+				ms1 = GetInt(buff);
+				hh2 = GetInt(buff, ':');
+				mm2 = GetInt(buff, ':');
+				ss2 = GetInt(buff, '.');
+				ms2 = GetInt(buff);
+				Style = WToT(GetStr(buff)) + _T("_") + WToT(GetStr(buff));
+				Actor = WToT(GetStr(buff));
+				marginRect.left = GetInt(buff);
+				marginRect.right = GetInt(buff);
+				marginRect.top = marginRect.bottom = GetInt(buff);
+
+				Style.TrimLeft('*');
+				if(!Style.CompareNoCase(_T("Default"))) Style = _T("Default");
+
+				ret.Add(buff,
+					file->IsUnicode(),
+					(((hh1*60 + mm1)*60) + ss1)*1000 + ms1, 
+					(((hh2*60 + mm2)*60) + ss2)*1000 + ms2, 
+					Style, Actor, _T(""),
+					marginRect,
+					layer);
+			}
+			catch(...)
+			{
+				return(false);
+			}
+		}
+		else if(entry == L"fontname")
+		{
+			LoadUUEFont(file);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+#include "USFSubtitles.h"
+
+static bool OpenUSF(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	CString str;
+	while(file->ReadString(str))
+	{
+		if(str.Find(_T("USFSubtitles")) >= 0)
+		{
+			CUSFSubtitles usf;
+			if(usf.Read(file->GetFilePath()) && usf.ConvertToSTS(ret))
+				return(true);
+
+			break;
+		}
+	}
+
+	return(false);
+}
+
+static CStringW MPL22SSA(CStringW str)
+{
+	CAtlList<CStringW> sl;
+	Explode(str, sl, '|');
+	POSITION pos = sl.GetHeadPosition();
+	while(pos)
+	{
+		CStringW& s = sl.GetNext(pos);
+		if(s[0] == '/') {s = L"{\\i1}" + s.Mid(1) + L"{\\i0}";}
+	}
+	str = Implode(sl, '\n');
+	str.Replace(L"\n", L"\\N");
+	return str;
+}
+
+static bool OpenMPL2(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet)
+{
+	CStringW buff;;
+	while(file->ReadString(buff))
+	{
+		buff.Trim();
+		if(buff.IsEmpty()) continue;
+
+		int start, end;
+		int c = swscanf(buff, L"[%d][%d]", &start, &end);
+
+		if(c == 2)
+		{
+			ret.Add(
+				MPL22SSA(buff.Mid(buff.Find(']', buff.Find(']')+1)+1)), 
+				file->IsUnicode(),
+				start*100, end*100);
+		}
+		else if(c != EOF) // might be another format
+		{
+			return(false);
+		}
+	}
+
+	return(ret.GetCount() > 0);
+}
+
+typedef bool (*STSOpenFunct)(CTextFile* file, CSimpleTextSubtitle& ret, int CharSet);
+
+typedef struct {STSOpenFunct open; tmode mode;} OpenFunctStruct;
+
+static OpenFunctStruct OpenFuncts[] = 
+{
+	OpenSubRipper, TIME,
+	OpenOldSubRipper, TIME,
+	OpenSubViewer, TIME,
+	OpenMicroDVD, FRAME,
+	OpenSami, TIME,
+	OpenVPlayer, TIME,
+	OpenSubStationAlpha, TIME,
+	OpenXombieSub, TIME,
+	OpenUSF, TIME,
+	OpenMPL2, TIME,
+};
+
+static int nOpenFuncts = countof(OpenFuncts);
+
+//
+
+CSimpleTextSubtitle::CSimpleTextSubtitle()
+{
+	m_mode = TIME;
+	m_dstScreenSize = CSize(0, 0);
+	m_defaultWrapStyle = 0;
+	m_collisions = 0;
+	m_fScaledBAS = false;
+	m_encoding = CTextFile::ASCII;
+}
+
+CSimpleTextSubtitle::~CSimpleTextSubtitle()
+{
+	Empty();
+}
+/*
+CSimpleTextSubtitle::CSimpleTextSubtitle(CSimpleTextSubtitle& sts)
+{
+	*this = sts;
+}
+
+CSimpleTextSubtitle& CSimpleTextSubtitle::operator = (CSimpleTextSubtitle& sts)
+{
+	Empty();
+
+	m_name = sts.m_name;
+	m_mode = sts.m_mode;
+	m_dstScreenSize = sts.m_dstScreenSize;
+	m_defaultWrapStyle = sts.m_defaultWrapStyle;
+	m_collisions = sts.m_collisions;
+	m_fScaledBAS = sts.m_fScaledBAS;
+	m_fSSA = sts.m_fSSA;
+	m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
+	CopyStyles(sts.m_styles);
+	m_segments.Copy(sts.m_segments);
+	Copy(sts);
+
+	return(*this);
+}
+*/
+
+void CSimpleTextSubtitle::Copy(CSimpleTextSubtitle& sts)
+{
+	Empty();
+
+	m_name = sts.m_name;
+	m_mode = sts.m_mode;
+	m_dstScreenSize = sts.m_dstScreenSize;
+	m_defaultWrapStyle = sts.m_defaultWrapStyle;
+	m_collisions = sts.m_collisions;
+	m_fScaledBAS = sts.m_fScaledBAS;
+	m_encoding = sts.m_encoding;
+	m_fUsingAutoGeneratedDefaultStyle = sts.m_fUsingAutoGeneratedDefaultStyle;
+	CopyStyles(sts.m_styles);
+	m_segments.Copy(sts.m_segments);
+	__super::Copy(sts);
+}
+
+void CSimpleTextSubtitle::Append(CSimpleTextSubtitle& sts, int timeoff)
+{
+	if(timeoff < 0) 
+	{
+		timeoff = GetCount() > 0 ? GetAt(GetCount()-1).end : 0;
+	}
+
+	for(int i = 0, j = GetCount(); i < j; i++)
+	{
+		if(GetAt(i).start > timeoff) 
+		{
+			RemoveAt(i, j - i);
+			break;
+		}
+	}
+
+	CopyStyles(sts.m_styles, true);
+
+	for(int i = 0, j = sts.GetCount(); i < j; i++)
+	{
+		STSEntry stse = sts.GetAt(i);
+		stse.start += timeoff;
+		stse.end += timeoff;
+		stse.readorder += GetCount();
+		__super::Add(stse);
+	}
+
+	CreateSegments();
+}
+
+void CSTSStyleMap::Free()
+{
+	POSITION pos = GetStartPosition();
+	while(pos) 
+	{
+		CString key;
+		STSStyle* val;
+		GetNextAssoc(pos, key, val);
+		delete val;
+	}
+
+	RemoveAll();
+}
+
+bool CSimpleTextSubtitle::CopyStyles(const CSTSStyleMap& styles, bool fAppend)
+{
+	if(!fAppend) m_styles.Free();
+
+	POSITION pos = styles.GetStartPosition();
+	while(pos) 
+	{
+		CString key;
+		STSStyle* val;
+		styles.GetNextAssoc(pos, key, val);
+
+		STSStyle* s = new STSStyle;
+		if(!s) return(false);
+
+		*s = *val;
+
+		AddStyle(key, s);
+	}
+
+	return(true);
+}
+
+void CSimpleTextSubtitle::Empty()
+{
+	m_dstScreenSize = CSize(0, 0);
+	m_styles.Free();
+	m_segments.RemoveAll();
+	RemoveAll();
+}
+
+void CSimpleTextSubtitle::Add(CStringW str, bool fUnicode, int start, int end, CString style, CString actor, CString effect, CRect marginRect, int layer, int readorder)
+{
+	if(str.Trim().IsEmpty() || start > end) return;
+
+	str.Remove('\r');
+	str.Replace(L"\n", L"\\N");
+	if(style.IsEmpty()) style = _T("Default");
+	style.TrimLeft('*');
+
+	STSEntry sub;
+	sub.str = str;
+	sub.fUnicode = fUnicode;
+	sub.style = style;
+	sub.actor = actor;
+	sub.effect = effect;
+	sub.marginRect = marginRect;
+	sub.layer = layer;
+	sub.start = start;
+	sub.end = end;
+	sub.readorder = readorder < 0 ? GetCount() : readorder;
+	int n = __super::Add(sub);
+
+	int len = m_segments.GetCount();
+
+    if(len == 0)
+	{
+		STSSegment stss(start, end);
+		stss.subs.Add(n);
+		m_segments.Add(stss);
+	}
+	else if(end <= m_segments[0].start)
+	{
+		STSSegment stss(start, end);
+		stss.subs.Add(n);
+		m_segments.InsertAt(0, stss);
+	}
+	else if(start >= m_segments[len-1].end)
+	{
+		STSSegment stss(start, end);
+		stss.subs.Add(n);
+		m_segments.Add(stss);
+	}
+	else
+	{
+		if(start < m_segments[0].start)
+		{
+			STSSegment stss(start, m_segments[0].start);
+			stss.subs.Add(n);
+			start = m_segments[0].start;
+			m_segments.InsertAt(0, stss);
+		}
+
+		for(int i = 0; i < m_segments.GetCount(); i++)
+		{
+			STSSegment& s = m_segments[i];
+
+			if(start >= s.end)
+				continue;
+
+			if(end <= s.start)
+				break;
+
+			if(s.start < start && start < s.end)
+			{
+				STSSegment stss(s.start, start);
+				stss.subs.Copy(s.subs);
+				s.start = start;
+				m_segments.InsertAt(i, stss);
+				continue;
+			}
+
+			if(start <= s.start && s.end <= end)
+			{
+				for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
+				{
+					if(j == k || sub.readorder < GetAt(s.subs[j]).readorder)
+						s.subs.InsertAt(j, n);
+				}
+//				s.subs.Add(n);
+			}
+			
+			if(s.start < end && end < s.end)
+			{
+				STSSegment stss(s.start, end);
+				stss.subs.Copy(s.subs);
+				for(int j = 0, k = s.subs.GetCount(); j <= k; j++)
+				{
+					if(j == k || sub.readorder < GetAt(stss.subs[j]).readorder)
+						stss.subs.InsertAt(j, n);
+				}
+//				stss.subs.Add(n);
+				s.start = end;
+				m_segments.InsertAt(i, stss);
+			}
+		}
+
+		if(end > m_segments[m_segments.GetCount()-1].end)
+		{
+			STSSegment stss(m_segments[m_segments.GetCount()-1].end, end);
+			stss.subs.Add(n);
+			m_segments.Add(stss);
+		}
+	}
+
+/*
+	str.Remove('\r');
+	str.Replace(L"\n", L"\\N");
+	if(style.IsEmpty()) style = _T("Default");
+
+	int j = m_segments.GetCount();
+	for(int i = j-1; i >= 0; i--)
+	{
+		if(m_segments[i].end <= start)
+		{
+			break;
+		}
+		else if(m_segments[i].start >= start)
+		{
+			m_segments.SetCount(m_segments.GetCount()-1);
+			j--;
+		}
+		else if(m_segments[i].end > start)
+		{
+			if(i < j-1) m_segments.RemoveAt(i+1, j-i-1);
+			m_segments[i].end = start;
+			break;
+		}
+	}
+
+	if(m_segments.GetCount() == 0 && j > 0) 
+		CSTSArray::RemoveAll();
+
+	STSSegment stss(start, end);
+	int len = GetCount();
+	stss.subs.Add(len);
+	m_segments.Add(stss);
+
+	STSEntry sub;
+	sub.str = str;
+	sub.fUnicode = fUnicode;
+	sub.style = style;
+	sub.actor = actor;
+	sub.effect = effect;
+	sub.marginRect = marginRect;
+	sub.layer = layer;
+	sub.start = start;
+	sub.end = end;
+	sub.readorder = GetCount();
+	CSTSArray::Add(sub);
+*/
+}
+
+STSStyle* CSimpleTextSubtitle::CreateDefaultStyle(int CharSet)
+{
+	CString def(_T("Default"));
+
+	STSStyle* ret = NULL;
+
+	if(!m_styles.Lookup(def, ret))
+	{
+		STSStyle* style = new STSStyle();
+		style->charSet = CharSet;
+		AddStyle(def, style);
+		m_styles.Lookup(def, ret);
+
+		m_fUsingAutoGeneratedDefaultStyle = true;
+	}
+	else
+	{
+		m_fUsingAutoGeneratedDefaultStyle = false;
+	}
+
+	return ret;
+}
+
+void CSimpleTextSubtitle::ChangeUnknownStylesToDefault()
+{
+	CAtlMap<CString, STSStyle*, CStringElementTraits<CString> > unknown;
+	bool fReport = true;
+
+	for(int i = 0; i < GetCount(); i++)
+	{
+		STSEntry& stse = GetAt(i);
+
+		STSStyle* val;
+		if(!m_styles.Lookup(stse.style, val))
+		{
+			if(!unknown.Lookup(stse.style, val))
+			{
+				if(fReport)
+				{
+					CString msg;
+					msg.Format(_T("Unknown style found: \"%s\", changed to \"Default\"!\n\nPress Cancel to ignore further warnings."), stse.style);
+					if(MessageBox(NULL, msg, _T("Warning"), MB_OKCANCEL|MB_ICONWARNING) != IDOK) fReport = false;
+				}
+
+				unknown[stse.style] = NULL;
+			}
+
+			stse.style = _T("Default");
+		}
+	}
+}
+
+void CSimpleTextSubtitle::AddStyle(CString name, STSStyle* style)
+{
+	int i, j;
+
+	if(name.IsEmpty()) name = _T("Default");
+
+	STSStyle* val;
+	if(m_styles.Lookup(name, val)) 
+	{
+		if(*val == *style)
+		{
+			delete style;
+			return;
+		}
+
+		int len = name.GetLength();
+
+		for(i = len; i > 0 && _istdigit(name[i-1]); i--);
+
+		int idx = 1;
+
+		CString name2 = name;
+
+		if(i < len && _stscanf(name.Right(len-i), _T("%d"), &idx) == 1)
+		{
+			name2 = name.Left(i);
+		}
+
+		idx++;
+
+		CString name3;
+		do
+		{
+			name3.Format(_T("%s%d"), name2, idx);
+			idx++;
+		}
+		while(m_styles.Lookup(name3));
+
+		m_styles.RemoveKey(name);
+		m_styles[name3] = val;
+
+		for(i = 0, j = GetCount(); i < j; i++)
+		{
+			STSEntry& stse = GetAt(i);
+			if(stse.style == name) stse.style = name3;
+		}
+	}
+
+	m_styles[name] = style;
+}
+
+bool CSimpleTextSubtitle::SetDefaultStyle(STSStyle& s)
+{
+	STSStyle* val;
+	if(!m_styles.Lookup(_T("Default"), val)) return false;
+	*val = s;
+	m_fUsingAutoGeneratedDefaultStyle = false;
+	return true;
+}
+
+bool CSimpleTextSubtitle::GetDefaultStyle(STSStyle& s)
+{
+	STSStyle* val;
+	if(!m_styles.Lookup(_T("Default"), val)) return false;
+	s = *val;
+	return true;
+}
+
+void CSimpleTextSubtitle::ConvertToTimeBased(double fps)
+{
+	if(m_mode == TIME) return;
+
+	for(int i = 0, j = GetCount(); i < j; i++)
+	{
+		STSEntry& stse = (*this)[i];
+		stse.start = int(1.0 * stse.start * 1000 / fps + 0.5);
+		stse.end = int(1.0 * stse.end * 1000 / fps + 0.5);
+	}
+
+	m_mode = TIME;
+
+	CreateSegments();
+}
+
+void CSimpleTextSubtitle::ConvertToFrameBased(double fps)
+{
+	if(m_mode == FRAME) return;
+
+	for(int i = 0, j = GetCount(); i < j; i++)
+	{
+		STSEntry& stse = (*this)[i];
+		stse.start = int(1.0 * stse.start * fps / 1000 + 0.5);
+		stse.end = int(1.0 * stse.end * fps / 1000 + 0.5);
+	}
+
+	m_mode = FRAME;
+
+	CreateSegments();
+}
+
+int CSimpleTextSubtitle::SearchSub(int t, double fps)
+{
+	int i = 0, j = GetCount() - 1, ret = -1;
+
+	if(j >= 0 && t >= TranslateStart(j, fps))
+	{
+		return(j);
+	}
+
+	while(i < j)
+	{
+		int mid = (i + j) >> 1;
+	
+		int midt = TranslateStart(mid, fps);
+
+		if(t == midt)
+		{
+			while(mid > 0 && t == TranslateStart(mid-1, fps)) mid--;
+			ret = mid;
+			break;
+		}
+		else if(t < midt)
+		{
+			ret = -1;
+			if(j == mid) mid--;
+			j = mid;
+		}
+		else if(t > midt)
+		{
+			ret = mid;
+			if(i == mid) mid++;
+			i = mid;
+		}
+	}
+
+	return(ret);
+}
+
+const STSSegment* CSimpleTextSubtitle::SearchSubs(int t, double fps, /*[out]*/ int* iSegment, int* nSegments)
+{
+	int i = 0, j = m_segments.GetCount() - 1, ret = -1;
+
+	if(nSegments) *nSegments = j+1;
+
+	if(j >= 0 && t >= TranslateSegmentStart(j, fps) && t < TranslateSegmentEnd(j, fps))
+	{
+		if(iSegment) *iSegment = j;
+		return(&m_segments[j]);
+	}
+
+	if(j >= 0 && t >= TranslateSegmentEnd(j, fps))
+	{
+		if(iSegment) *iSegment = j+1;
+		return(NULL);
+	}
+
+	if(j > 0 && t < TranslateSegmentStart(i, fps))
+	{
+		if(iSegment) *iSegment = -1;
+		return(NULL);
+	}
+
+	while(i < j)
+	{
+		int mid = (i + j) >> 1;
+	
+		int midt = TranslateSegmentStart(mid, fps);
+
+		if(t == midt)
+		{
+			ret = mid;
+			break;
+		}
+		else if(t < midt)
+		{
+			ret = -1;
+			if(j == mid) mid--;
+			j = mid;
+		}
+		else if(t > midt)
+		{
+			ret = mid;
+			if(i == mid) mid++;
+			i = mid;
+		}
+	}
+
+	if(0 <= ret && ret < m_segments.GetCount())
+	{
+		if(iSegment) *iSegment = ret;
+	}
+
+	if(0 <= ret && ret < m_segments.GetCount() 
+	&& m_segments[ret].subs.GetCount() > 0
+	&& TranslateSegmentStart(ret, fps) <= t && t < TranslateSegmentEnd(ret, fps))
+	{
+		return(&m_segments[ret]);
+	}
+
+	return(NULL);
+}
+
+int CSimpleTextSubtitle::TranslateStart(int i, double fps)
+{
+	return(i < 0 || GetCount() <= i ? -1 :
+		m_mode == TIME ? GetAt(i).start :	
+		m_mode == FRAME ? (int)(GetAt(i).start*1000/fps) :
+		0);
+}
+
+int CSimpleTextSubtitle::TranslateEnd(int i, double fps)
+{
+	return(i < 0 || GetCount() <= i ? -1 :
+		m_mode == TIME ? GetAt(i).end :
+		m_mode == FRAME ? (int)(GetAt(i).end*1000/fps) :
+		0);
+}
+
+int CSimpleTextSubtitle::TranslateSegmentStart(int i, double fps)
+{
+	return(i < 0 || m_segments.GetCount() <= i ? -1 :
+		m_mode == TIME ? m_segments[i].start :	
+		m_mode == FRAME ? (int)(m_segments[i].start*1000/fps) :
+		0);
+}
+
+int CSimpleTextSubtitle::TranslateSegmentEnd(int i, double fps)
+{
+	return(i < 0 || m_segments.GetCount() <= i ? -1 :
+		m_mode == TIME ? m_segments[i].end :
+		m_mode == FRAME ? (int)(m_segments[i].end*1000/fps) :
+		0);
+}
+
+STSStyle* CSimpleTextSubtitle::GetStyle(int i)
+{
+	CString def = _T("Default");
+
+	STSStyle* style = NULL;
+	m_styles.Lookup(GetAt(i).style, style);
+
+	STSStyle* defstyle = NULL;
+	m_styles.Lookup(def, defstyle);
+
+	if(!style)
+	{
+		style = defstyle;
+	}
+
+	ASSERT(style);
+
+	return style;
+}
+
+bool CSimpleTextSubtitle::GetStyle(int i, STSStyle& stss)
+{
+	CString def = _T("Default");
+
+	STSStyle* style = NULL;
+	m_styles.Lookup(GetAt(i).style, style);
+
+	STSStyle* defstyle = NULL;
+	m_styles.Lookup(def, defstyle);
+
+	if(!style)
+	{
+		if(!defstyle)
+		{
+			defstyle = CreateDefaultStyle(DEFAULT_CHARSET); 
+		}
+
+		style = defstyle;
+	}
+
+	if(!style) {ASSERT(0); return false;}
+
+	stss = *style;
+	if(stss.relativeTo == 2 && defstyle)
+		stss.relativeTo = defstyle->relativeTo;
+
+	return true;
+}
+
+int CSimpleTextSubtitle::GetCharSet(int i)
+{
+	STSStyle stss;
+	GetStyle(i, stss);
+	return(stss.charSet);
+}
+
+bool CSimpleTextSubtitle::IsEntryUnicode(int i)
+{
+	return(GetAt(i).fUnicode);
+}
+
+void CSimpleTextSubtitle::ConvertUnicode(int i, bool fUnicode)
+{
+	STSEntry& stse = GetAt(i);
+
+	if(stse.fUnicode ^ fUnicode)
+	{
+		int CharSet = GetCharSet(i);
+
+		stse.str = fUnicode 
+			? MBCSSSAToUnicode(stse.str, CharSet)
+			: UnicodeSSAToMBCS(stse.str, CharSet);
+
+		stse.fUnicode = fUnicode;
+	}
+}
+
+CStringA CSimpleTextSubtitle::GetStrA(int i, bool fSSA)
+{
+	return(WToA(GetStrWA(i, fSSA)));
+}
+
+CStringW CSimpleTextSubtitle::GetStrW(int i, bool fSSA)
+{
+	bool fUnicode = IsEntryUnicode(i);
+	int CharSet = GetCharSet(i);
+
+	CStringW str = GetAt(i).str;
+
+	if(!fUnicode)
+		str = MBCSSSAToUnicode(str, CharSet);
+
+	if(!fSSA)
+		str = RemoveSSATags(str, fUnicode, CharSet);
+
+	return(str);
+}
+
+CStringW CSimpleTextSubtitle::GetStrWA(int i, bool fSSA)
+{
+	bool fUnicode = IsEntryUnicode(i);
+	int CharSet = GetCharSet(i);
+
+	CStringW str = GetAt(i).str;
+
+	if(fUnicode)
+		str = UnicodeSSAToMBCS(str, CharSet);
+
+	if(!fSSA)
+		str = RemoveSSATags(str, fUnicode, CharSet);
+
+	return(str);
+}
+
+void CSimpleTextSubtitle::SetStr(int i, CStringA str, bool fUnicode)
+{
+	SetStr(i, AToW(str), false);
+}
+
+void CSimpleTextSubtitle::SetStr(int i, CStringW str, bool fUnicode)
+{
+	STSEntry& stse = GetAt(i);
+
+	str.Replace(L"\n", L"\\N"); 
+
+	if(stse.fUnicode && !fUnicode) stse.str = MBCSSSAToUnicode(str, GetCharSet(i));
+	else if(!stse.fUnicode && fUnicode) stse.str = UnicodeSSAToMBCS(str, GetCharSet(i));
+	else stse.str = str;
+}
+
+static int comp1(const void* a, const void* b)
+{
+	int ret = ((STSEntry*)a)->start - ((STSEntry*)b)->start;
+	if(ret == 0) ret = ((STSEntry*)a)->layer - ((STSEntry*)b)->layer;
+	if(ret == 0) ret = ((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder;
+	return(ret);
+}
+
+static int comp2(const void* a, const void* b)
+{
+	return(((STSEntry*)a)->readorder - ((STSEntry*)b)->readorder);
+}
+
+void CSimpleTextSubtitle::Sort(bool fRestoreReadorder)
+{
+	qsort(GetData(), GetCount(), sizeof(STSEntry), !fRestoreReadorder ? comp1 : comp2);
+	CreateSegments();
+}
+
+static int intcomp(const void* i1, const void* i2)
+{
+	return(*((int*)i1) - *((int*)i2));
+}
+
+void CSimpleTextSubtitle::CreateSegments()
+{
+	m_segments.RemoveAll();
+
+	int i, j;
+
+	CAtlArray<int> breakpoints;
+
+	for(i = 0; i < GetCount(); i++)
+	{
+		STSEntry& stse = GetAt(i);
+		breakpoints.Add(stse.start);
+		breakpoints.Add(stse.end);
+	}
+
+	qsort(breakpoints.GetData(), breakpoints.GetCount(), sizeof(int), intcomp);
+
+	int* ptr = breakpoints.GetData(), prev = ptr ? *ptr : NULL;
+
+	for(i = breakpoints.GetCount(); i > 0; i--, ptr++)
+	{
+		if(*ptr != prev) 
+		{
+			m_segments.Add(STSSegment(prev, *ptr));
+			prev = *ptr;
+		}
+	}
+
+	for(i = 0; i < GetCount(); i++)
+	{
+		STSEntry& stse = GetAt(i);
+		for(j = 0; j < m_segments.GetCount() && m_segments[j].start < stse.start; j++);
+		for(; j < m_segments.GetCount() && m_segments[j].end <= stse.end; j++) 
+			m_segments[j].subs.Add(i);
+	}
+
+	OnChanged();
+/*
+	for(i = 0, j = m_segments.GetCount(); i < j; i++)
+	{
+		STSSegment& stss = m_segments[i];
+
+		TRACE(_T("%d - %d"), stss.start, stss.end);
+
+		for(int k = 0, l = stss.subs.GetCount(); k < l; k++)
+		{
+			TRACE(_T(", %d"), stss.subs[k]);
+		}
+
+		TRACE(_T("\n"));
+	}
+*/
+}
+
+bool CSimpleTextSubtitle::Open(CString fn, int CharSet, CString name)
+{
+	Empty();
+
+	CWebTextFile f;
+	if(!f.Open(fn)) return(false);
+
+	fn.Replace('\\', '/');
+	if(name.IsEmpty())
+	{
+		name = fn.Left(fn.ReverseFind('.'));
+		name = name.Mid(name.ReverseFind('/')+1);
+		name = name.Mid(name.ReverseFind('.')+1);
+	}
+
+	return(Open(&f, CharSet, name));
+}
+
+static int CountLines(CTextFile* f, ULONGLONG from, ULONGLONG to)
+{
+	int n = 0;
+	CString s;
+	f->Seek(from, 0);
+	while(f->ReadString(s) && f->GetPosition() < to) n++;
+	return(n);
+}
+
+bool CSimpleTextSubtitle::Open(CTextFile* f, int CharSet, CString name)
+{
+	Empty();
+
+	ULONGLONG pos = f->GetPosition();
+
+	for(int i = 0; i < nOpenFuncts; i++)
+	{
+		if(!OpenFuncts[i].open(f, *this, CharSet) /*|| !GetCount()*/) 
+		{
+			if(GetCount() > 0)
+			{
+				int n = CountLines(f, pos, f->GetPosition());
+				CString s;
+				s.Format(_T("Syntax error at line %d!\t"), n+1);
+				AfxMessageBox(s, MB_OK|MB_ICONERROR);
+				Empty();
+				break;
+			}
+
+			f->Seek(pos, 0);
+			Empty();
+			continue;
+		}
+
+		m_name = name;
+		m_mode = OpenFuncts[i].mode;
+		m_encoding = f->GetEncoding();
+		m_path = f->GetFilePath();
+
+//		Sort();
+		CreateSegments();
+
+		CWebTextFile f2;
+		if(f2.Open(f->GetFilePath() + _T(".style"))) 
+			OpenSubStationAlpha(&f2, *this, CharSet);
+
+		CreateDefaultStyle(CharSet);
+
+		ChangeUnknownStylesToDefault();
+
+		if(m_dstScreenSize == CSize(0, 0)) m_dstScreenSize = CSize(384, 288);
+
+		return(true);
+	}
+
+	return(false);
+}
+
+bool CSimpleTextSubtitle::Open(BYTE* data, int len, int CharSet, CString name)
+{
+	TCHAR path[MAX_PATH];
+	if(!GetTempPath(MAX_PATH, path)) return(false);
+
+	TCHAR fn[MAX_PATH];
+	if(!GetTempFileName(path, _T("vs"), 0, fn)) return(false);
+
+	FILE* tmp = _tfopen(fn, _T("wb"));
+	if(!tmp) return(false);
+
+	int i = 0;
+	for(; i <= (len-1024); i += 1024) fwrite(&data[i], 1024, 1, tmp);
+	if(len > i) fwrite(&data[i], len - i, 1, tmp);
+
+	fclose(tmp);
+
+	bool fRet = Open(fn, CharSet, name);
+
+	_tremove(fn);
+
+	return(fRet);
+}
+
+bool CSimpleTextSubtitle::SaveAs(CString fn, exttype et, double fps, CTextFile::enc e)
+{
+	if(fn.Mid(fn.ReverseFind('.')+1).CompareNoCase(exttypestr[et])) 
+	{
+		if(fn[fn.GetLength()-1] != '.') fn += _T("."); 
+		fn += exttypestr[et];
+	}
+
+	CTextFile f;
+	if(!f.Save(fn, e))
+		return(false);
+
+	if(et == EXTSMI)
+	{
+		CString str;
+
+		str += _T("<SAMI>\n<HEAD>\n");
+		str += _T("<STYLE TYPE=\"text/css\">\n");
+		str += _T("<!--\n");
+		str += _T("P {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\n");
+		str += _T("   text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\n");
+		str += _T(".UNKNOWNCC {Name:Unknown; lang:en-US; SAMIType:CC;}\n");
+		str += _T("-->\n");
+		str += _T("</STYLE>\n");
+		str += _T("</HEAD>\n");
+		str += _T("\n");
+		str += _T("<BODY>\n");
+
+		f.WriteString(str);
+	}
+	else if(et == EXTSSA || et == EXTASS)
+	{
+		CString str;
+
+		str  = _T("[Script Info]\n");
+		str += (et == EXTSSA) ? _T("; This is a Sub Station Alpha v4 script.\n") : _T("; This is an Advanced Sub Station Alpha v4+ script.\n");
+		str += _T("; For Sub Station Alpha info and downloads,\n");
+		str += _T("; go to http://www.eswat.demon.co.uk/\n");
+		str += _T("; or email kotus@eswat.demon.co.uk\n");
+		str += _T("; \n");
+		if(et == EXTASS) 
+		{
+			str += _T("; Advanced Sub Station Alpha script format developed by #Anime-Fansubs@EfNET\n");
+			str += _T("; http://www.anime-fansubs.org\n");
+			str += _T("; \n");
+			str += _T("; For additional info and downloads go to http://gabest.org/\n");
+			str += _T("; or email gabest@freemail.hu\n");
+			str += _T("; \n");
+		}
+		str += _T("; Note: This file was saved by Subresync.\n");
+		str += _T("; \n");
+		str += (et == EXTSSA) ? _T("ScriptType: v4.00\n") : _T("ScriptType: v4.00+\n");
+		str += (m_collisions == 0) ? _T("Collisions: Normal\n") : _T("Collisions: Reverse\n");
+		if(et == EXTASS && m_fScaledBAS) str += _T("ScaledBorderAndShadow: Yes\n");
+		str += _T("PlayResX: %d\n");
+		str += _T("PlayResY: %d\n");
+		str += _T("Timer: 100.0000\n");
+		str += _T("\n");
+		str += (et == EXTSSA) 
+			? _T("[V4 Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding\n")
+			: _T("[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n");
+
+		CString str2;
+		str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
+		f.WriteString(str2);
+
+		str  = (et == EXTSSA)
+			? _T("Style: %s,%s,%d,&H%06x,&H%06x,&H%06x,&H%06x,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n")
+			: _T("Style: %s,%s,%d,&H%08x,&H%08x,&H%08x,&H%08x,%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%d,%d,%d,%d,%d,%d,%d\n");
+
+		POSITION pos = m_styles.GetStartPosition();
+		while(pos) 
+		{
+			CString key;
+			STSStyle* s;
+			m_styles.GetNextAssoc(pos, key, s);
+
+			if(et == EXTSSA)
+			{
+				CString str2;
+				str2.Format(str, key,
+					s->fontName, (int)s->fontSize,
+					s->colors[0]&0xffffff, 
+					s->colors[1]&0xffffff, 
+					s->colors[2]&0xffffff, 
+					s->colors[3]&0xffffff, 
+					s->fontWeight > FW_NORMAL ? -1 : 0, s->fItalic ? -1 : 0,
+					s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, 
+					(int)s->outlineWidth, (int)s->shadowDepth, 
+					s->scrAlignment <= 3 ? s->scrAlignment : s->scrAlignment <= 6 ? ((s->scrAlignment-3)|8) : s->scrAlignment <= 9 ? ((s->scrAlignment-6)|4) : 2,
+					s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2,
+					s->alpha[0],
+					s->charSet);
+				f.WriteString(str2);
+			}
+			else
+			{
+				CString str2;
+				str2.Format(str, key,
+					s->fontName, (int)s->fontSize,
+					(s->colors[0]&0xffffff) | (s->alpha[0]<<24),
+					(s->colors[1]&0xffffff) | (s->alpha[1]<<24),
+					(s->colors[2]&0xffffff) | (s->alpha[2]<<24),
+					(s->colors[3]&0xffffff) | (s->alpha[3]<<24),
+					s->fontWeight > FW_NORMAL ? -1 : 0, 
+					s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0, 
+					(int)s->fontScaleX, (int)s->fontScaleY,
+					(int)s->fontSpacing, (float)s->fontAngleZ,
+					s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, 
+					(int)s->outlineWidth, (int)s->shadowDepth, 
+					s->scrAlignment,
+					s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2,
+					s->charSet);
+				f.WriteString(str2);
+			}
+		}
+
+		if(GetCount() > 0)
+		{
+			str  = _T("\n");
+			str += _T("[Events]\n");
+			str += (et == EXTSSA) 
+				? _T("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
+				: _T("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text\n");
+			f.WriteString(str);
+		}
+	}
+
+	CStringW fmt = 
+		et == EXTSRT ? L"%d\n%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\n%s\n\n" :
+		et == EXTSUB ? L"{%d}{%d}%s\n" :
+		et == EXTSMI ? L"<SYNC Start=%d><P Class=UNKNOWNCC>\n%s\n<SYNC Start=%d><P Class=UNKNOWNCC>&nbsp;\n" :
+		et == EXTPSB ? L"{%d:%02d:%02d}{%d:%02d:%02d}%s\n" :
+		et == EXTSSA ? L"Dialogue: Marked=0,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
+		et == EXTASS ? L"Dialogue: %d,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s,%s,%04d,%04d,%04d,%s,%s\n" :
+					   L"";
+//	Sort(true);
+
+	for(int i = 0, j = GetCount(), k = 0; i < j; i++)
+	{
+		STSEntry& stse = GetAt(i);
+		
+		int t1 = TranslateStart(i, fps);
+		if(t1 < 0) {k++; continue;}
+		
+		int t2 = TranslateEnd(i, fps);
+
+		int hh1 = (t1/60/60/1000);
+		int mm1 = (t1/60/1000)%60;
+		int ss1 = (t1/1000)%60;
+		int ms1 = (t1)%1000;
+		int hh2 = (t2/60/60/1000);
+		int mm2 = (t2/60/1000)%60;
+		int ss2 = (t2/1000)%60;
+		int ms2 = (t2)%1000;
+
+		CStringW str = f.IsUnicode()
+			? GetStrW(i, et == EXTSSA || et == EXTASS)
+			: GetStrWA(i, et == EXTSSA || et == EXTASS);
+
+		CStringW str2;
+
+		if(et == EXTSRT)
+		{
+			str2.Format(fmt, i-k+1, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, str);
+		}
+		else if(et == EXTSUB)
+		{
+			str.Replace('\n', '|');
+			str2.Format(fmt, int(t1*fps/1000), int(t2*fps/1000), str);
+		}
+		else if(et == EXTSMI)
+		{
+			str.Replace(L"\n", L"<br>");
+			str2.Format(fmt, t1, str, t2);
+		}
+		else if(et == EXTPSB)
+		{
+			str.Replace('\n', '|');
+			str2.Format(fmt, hh1, mm1, ss1, hh2, mm2, ss2, str);
+		}
+		else if(et == EXTSSA)
+		{
+			str.Replace(L"\n", L"\\N");
+			str2.Format(fmt, 
+				hh1, mm1, ss1, ms1/10,
+				hh2, mm2, ss2, ms2/10,
+				TToW(stse.style), TToW(stse.actor), 
+				stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
+				TToW(stse.effect), str);
+		}
+		else if(et == EXTASS)
+		{
+			str.Replace(L"\n", L"\\N");
+			str2.Format(fmt, 
+				stse.layer, 
+				hh1, mm1, ss1, ms1/10,
+				hh2, mm2, ss2, ms2/10,
+				TToW(stse.style), TToW(stse.actor), 
+				stse.marginRect.left, stse.marginRect.right, (stse.marginRect.top + stse.marginRect.bottom) / 2,
+				TToW(stse.effect), str);
+		}
+
+		f.WriteString(str2);
+	}
+
+//	Sort();
+
+	if(et == EXTSMI)
+	{
+		f.WriteString(_T("</BODY>\n</SAMI>\n"));
+	}
+
+	STSStyle* s;
+	if(!m_fUsingAutoGeneratedDefaultStyle && m_styles.Lookup(_T("Default"), s) && et != EXTSSA && et != EXTASS)
+	{
+		CTextFile f;
+		if(!f.Save(fn + _T(".style"), e))
+			return(false);
+
+		CString str, str2;
+
+		str += _T("ScriptType: v4.00+\n");
+		str += _T("PlayResX: %d\n");
+		str += _T("PlayResY: %d\n");
+		str += _T("\n");
+		str += _T("[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n");
+		str2.Format(str, m_dstScreenSize.cx, m_dstScreenSize.cy);
+		f.WriteString(str2);
+
+		str  = _T("Style: Default,%s,%d,&H%08x,&H%08x,&H%08x,&H%08x,%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%d,%d,%d,%d,%d,%d,%d\n");
+		str2.Format(str,
+			s->fontName, (int)s->fontSize,
+			(s->colors[0]&0xffffff) | (s->alpha[0]<<24),
+			(s->colors[1]&0xffffff) | (s->alpha[1]<<24),
+			(s->colors[2]&0xffffff) | (s->alpha[2]<<24),
+			(s->colors[3]&0xffffff) | (s->alpha[3]<<24),
+			s->fontWeight > FW_NORMAL ? -1 : 0, 
+			s->fItalic ? -1 : 0, s->fUnderline ? -1 : 0, s->fStrikeOut ? -1 : 0, 
+			(int)s->fontScaleX, (int)s->fontScaleY,
+			(int)s->fontSpacing, (float)s->fontAngleZ,
+			s->borderStyle == 0 ? 1 : s->borderStyle == 1 ? 3 : 0, 
+			(int)s->outlineWidth, (int)s->shadowDepth, 
+			s->scrAlignment,
+			s->marginRect.left, s->marginRect.right, (s->marginRect.top + s->marginRect.bottom) / 2,
+			s->charSet);
+		f.WriteString(str2);
+	}
+	
+	return(true);
+}
+
+////////////////////////////////////////////////////////////////////
+
+STSStyle::STSStyle()
+{
+	SetDefault();
+}
+
+void STSStyle::SetDefault()
+{
+	marginRect = CRect(20, 20, 20, 20);
+	scrAlignment = 2;
+	borderStyle = 0;
+	outlineWidth = 2;
+	shadowDepth = 3;
+	colors[0] = 0x00ffffff;
+	colors[1] = 0x0000ffff;
+	colors[2] = 0x00000000;
+	colors[3] = 0x00000000;
+	alpha[0] = 0x00;
+	alpha[1] = 0x00;
+	alpha[2] = 0x00;
+	alpha[3] = 0x80;
+    charSet = DEFAULT_CHARSET;
+	fontName = _T("Arial");
+	fontSize = 18;
+	fontScaleX = fontScaleY = 100;
+	fontSpacing = 0;
+	fontWeight = FW_BOLD;
+	fItalic = false;
+	fUnderline = false;
+	fStrikeOut = false;
+	fBlur = false;
+	fontShiftX = fontShiftY = fontAngleZ = fontAngleX = fontAngleY = 0;
+	relativeTo = 2;
+}
+
+bool STSStyle::operator == (STSStyle& s)
+{
+	return(marginRect == s.marginRect 
+		&& scrAlignment == s.scrAlignment
+		&& borderStyle == s.borderStyle
+		&& outlineWidth == s.outlineWidth
+		&& shadowDepth == s.shadowDepth 
+		&& *((int*)&colors[0]) == *((int*)&s.colors[0])
+		&& *((int*)&colors[1]) == *((int*)&s.colors[1])
+		&& *((int*)&colors[2]) == *((int*)&s.colors[2])
+		&& *((int*)&colors[3]) == *((int*)&s.colors[3])
+		&& alpha[0] == s.alpha[0]
+		&& alpha[1] == s.alpha[1]
+		&& alpha[2] == s.alpha[2]
+		&& alpha[3] == s.alpha[3]
+		&& fBlur == s.fBlur
+		&& relativeTo == s.relativeTo
+		&& IsFontStyleEqual(s));
+}
+
+bool STSStyle::IsFontStyleEqual(STSStyle& s)
+{
+	return(
+		charSet == s.charSet
+		&& fontName == s.fontName
+		&& fontSize == s.fontSize
+		&& fontScaleX == s.fontScaleX
+		&& fontScaleY == s.fontScaleY
+		&& fontSpacing == s.fontSpacing
+		&& fontWeight == s.fontWeight
+		&& fItalic == s.fItalic
+		&& fUnderline == s.fUnderline
+		&& fStrikeOut == s.fStrikeOut
+		&& fontAngleZ == s.fontAngleZ
+		&& fontAngleX == s.fontAngleX
+		&& fontAngleY == s.fontAngleY);
+}
+
+void STSStyle::operator = (LOGFONT& lf)
+{
+	charSet = lf.lfCharSet;
+	fontName = lf.lfFaceName;
+	HDC hDC = GetDC(0);
+	//fontSize = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDC, LOGPIXELSY));
+	fontSize = lf.lfHeight;
+	ReleaseDC(0, hDC);
+//	fontAngleZ = (float)(1.0*lf.lfEscapement/10);
+	fontWeight = lf.lfWeight;
+	fItalic = !!lf.lfItalic;
+	fUnderline = !!lf.lfUnderline;
+	fStrikeOut = !!lf.lfStrikeOut;
+}
+
+LOGFONTA& operator <<= (LOGFONTA& lfa, STSStyle& s)
+{
+	lfa.lfCharSet = s.charSet;
+	strncpy_s(lfa.lfFaceName, LF_FACESIZE, CStringA(s.fontName), _TRUNCATE);
+	HDC hDC = GetDC(0);
+	//lfa.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
+	lfa.lfHeight = (LONG)(s.fontSize + 0.5);
+	ReleaseDC(0, hDC);
+	lfa.lfWeight = s.fontWeight;
+	lfa.lfItalic = s.fItalic?-1:0;
+	lfa.lfUnderline = s.fUnderline?-1:0;
+	lfa.lfStrikeOut = s.fStrikeOut?-1:0;
+	return(lfa);
+}
+
+LOGFONTW& operator <<= (LOGFONTW& lfw, STSStyle& s)
+{
+	lfw.lfCharSet = s.charSet;
+	wcsncpy_s(lfw.lfFaceName, LF_FACESIZE, CStringW(s.fontName), _TRUNCATE);
+	HDC hDC = GetDC(0);
+	//lfw.lfHeight = -MulDiv((int)(s.fontSize+0.5), GetDeviceCaps(hDC, LOGPIXELSY), 72);
+	lfw.lfHeight = (LONG)(s.fontSize + 0.5);
+	ReleaseDC(0, hDC);
+	lfw.lfWeight = s.fontWeight;
+	lfw.lfItalic = s.fItalic?-1:0;
+	lfw.lfUnderline = s.fUnderline?-1:0;
+	lfw.lfStrikeOut = s.fStrikeOut?-1:0;
+	return(lfw);
+}
+
+CString& operator <<= (CString& style, STSStyle& s)
+{
+	style.Format(_T("%d,%d,%d,%d,%d,%d,%f,%f,0x%06x,0x%06x,0x%06x,0x%06x,0x%02x,0x%02x,0x%02x,0x%02x,%d,%s,%f,%f,%f,%f,%d,%d,%d,%d,%d,%f,%f,%f,%d"),
+		s.marginRect.left,s.marginRect.right,s.marginRect.top,s.marginRect.bottom,
+		s.scrAlignment, s.borderStyle, s.outlineWidth, s.shadowDepth,
+		s.colors[0], s.colors[1], s.colors[2], s.colors[3], s.alpha[0], s.alpha[1], s.alpha[2], s.alpha[3],
+		s.charSet,
+		s.fontName, s.fontSize, s.fontScaleX, s.fontScaleY, s.fontSpacing, s.fontWeight,
+		(int)s.fItalic, (int)s.fUnderline, (int)s.fStrikeOut, (int)s.fBlur,
+		s.fontAngleZ, s.fontAngleX, s.fontAngleY,
+		s.relativeTo);
+
+	return(style);
+}
+
+STSStyle& operator <<= (STSStyle& s, CString& style)
+{
+	s.SetDefault();
+
+	try 
+	{
+		CStringW str = TToW(style);
+		s.marginRect.left = GetInt(str); s.marginRect.right = GetInt(str); s.marginRect.top = GetInt(str); s.marginRect.bottom = GetInt(str);
+		s.scrAlignment = GetInt(str); s.borderStyle = GetInt(str); s.outlineWidth = GetFloat(str); s.shadowDepth = GetFloat(str);
+		for(int i = 0; i < 4; i++) s.colors[i] = (COLORREF)GetInt(str);
+		for(int i = 0; i < 4; i++) s.alpha[i] = GetInt(str);
+		s.charSet = GetInt(str);
+		s.fontName = WToT(GetStr(str)); s.fontSize = GetFloat(str); 
+		s.fontScaleX = GetFloat(str); s.fontScaleY = GetFloat(str);
+		s.fontSpacing = GetFloat(str); s.fontWeight = GetInt(str);
+		s.fItalic = !!GetInt(str); s.fUnderline = !!GetInt(str); s.fStrikeOut = !!GetInt(str); s.fBlur = !!GetInt(str);
+		s.fontAngleZ = GetFloat(str); s.fontAngleX = GetFloat(str); s.fontAngleY = GetFloat(str);
+		s.relativeTo = GetInt(str);
+	}
+	catch(...)
+	{
+		s.SetDefault();
+	}
+
+	return(s);
+}
+
+
diff --git a/vsfilter/subtitles/STS.h b/vsfilter/subtitles/STS.h
new file mode 100644
index 0000000000000000000000000000000000000000..b7c7c24d59a853e4ef16cfbaf4d6e82b29c8c1eb
--- /dev/null
+++ b/vsfilter/subtitles/STS.h
@@ -0,0 +1,191 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include <wxutil.h>
+#include "TextFile.h"
+#include "GFN.h"
+
+typedef enum {TIME, FRAME} tmode; // the meaning of STSEntry::start/end
+
+class STSStyle 
+{
+public:
+	CRect	marginRect; // measured from the sides
+	int		scrAlignment; // 1 - 9: as on the numpad, 0: default
+	int		borderStyle; // 0: outline, 1: opaque box
+	double	outlineWidth;
+	double	shadowDepth;
+	COLORREF colors[4]; // usually: {primary, secondary, outline/background, shadow}
+	BYTE	alpha[4];
+    int		charSet;
+    CString fontName;
+	double	fontSize; // height
+	double	fontScaleX, fontScaleY; // percent
+	double	fontSpacing; // +/- pixels
+	int		fontWeight;
+	bool	fItalic;
+	bool	fUnderline;
+	bool	fStrikeOut;
+	bool	fBlur;
+	double	fontAngleZ, fontAngleX, fontAngleY;
+	double	fontShiftX, fontShiftY;
+	int		relativeTo; // 0: window, 1: video, 2: undefined (~window)
+
+	STSStyle();
+
+	void SetDefault();
+
+	bool operator == (STSStyle& s);
+	bool IsFontStyleEqual(STSStyle& s);
+
+	void operator = (LOGFONT& lf);
+
+	friend LOGFONTA& operator <<= (LOGFONTA& lfa, STSStyle& s);
+	friend LOGFONTW& operator <<= (LOGFONTW& lfw, STSStyle& s);
+
+	friend CString& operator <<= (CString& style, STSStyle& s);
+	friend STSStyle& operator <<= (STSStyle& s, CString& style);
+};
+
+class CSTSStyleMap : public CAtlMap<CString, STSStyle*, CStringElementTraits<CString> >
+{
+public:
+	CSTSStyleMap() {}
+	virtual ~CSTSStyleMap() {Free();}
+	void Free();
+};
+
+typedef struct 
+{
+	CStringW str;
+	bool fUnicode;
+	CString style, actor, effect;
+	CRect marginRect;
+	int layer;
+	int start, end;
+	int readorder;
+} STSEntry;
+
+class STSSegment
+{
+public:
+	int start, end;
+	CAtlArray<int> subs;
+
+	STSSegment() {};
+	STSSegment(int s, int e) {start = s; end = e;}
+	STSSegment(const STSSegment& stss) {*this = stss;}
+	void operator = (const STSSegment& stss) {start = stss.start; end = stss.end; subs.Copy(stss.subs);}
+};
+
+class CSimpleTextSubtitle : public CAtlArray<STSEntry>
+{
+	friend class CSubtitleEditorDlg;
+
+protected:
+	CAtlArray<STSSegment> m_segments;
+	virtual void OnChanged() {}
+
+public:
+	CString m_name;
+	tmode m_mode;
+	CTextFile::enc m_encoding;
+	CString m_path;
+
+	CSize m_dstScreenSize;
+	int m_defaultWrapStyle;
+	int m_collisions;
+	bool m_fScaledBAS;
+
+	bool m_fUsingAutoGeneratedDefaultStyle;
+
+	CSTSStyleMap m_styles;
+
+public:
+	CSimpleTextSubtitle();
+	virtual ~CSimpleTextSubtitle();
+
+	virtual void Copy(CSimpleTextSubtitle& sts);
+	virtual void Empty();
+
+	void Sort(bool fRestoreReadorder = false);
+	void CreateSegments();
+
+	void Append(CSimpleTextSubtitle& sts, int timeoff = -1);
+
+	bool Open(CString fn, int CharSet, CString name = _T(""));
+	bool Open(CTextFile* f, int CharSet, CString name); 
+	bool Open(BYTE* data, int len, int CharSet, CString name); 
+	bool SaveAs(CString fn, exttype et, double fps = -1, CTextFile::enc = CTextFile::ASCII);
+
+	void Add(CStringW str, bool fUnicode, int start, int end, CString style = _T("Default"), CString actor = _T(""), CString effect = _T(""), CRect marginRect = CRect(0,0,0,0), int layer = 0, int readorder = -1);
+
+	STSStyle* CreateDefaultStyle(int CharSet);
+	void ChangeUnknownStylesToDefault();
+	void AddStyle(CString name, STSStyle* style); // style will be stored and freed in Empty() later
+	bool CopyStyles(const CSTSStyleMap& styles, bool fAppend = false);
+
+	bool SetDefaultStyle(STSStyle& s);
+	bool GetDefaultStyle(STSStyle& s);
+
+	void ConvertToTimeBased(double fps);
+	void ConvertToFrameBased(double fps);
+
+	int TranslateStart(int i, double fps); 
+	int TranslateEnd(int i, double fps);
+	int SearchSub(int t, double fps);
+
+	int TranslateSegmentStart(int i, double fps); 
+	int TranslateSegmentEnd(int i, double fps);
+	const STSSegment* SearchSubs(int t, double fps, /*[out]*/ int* iSegment = NULL, int* nSegments = NULL);
+	const STSSegment* GetSegment(int iSegment) {return iSegment >= 0 && iSegment < (int)m_segments.GetCount() ? &m_segments[iSegment] : NULL;}
+
+	STSStyle* GetStyle(int i);
+	bool GetStyle(int i, STSStyle& stss);
+	int GetCharSet(int i);
+	bool IsEntryUnicode(int i);
+	void ConvertUnicode(int i, bool fUnicode);
+
+	CStringA GetStrA(int i, bool fSSA = false);
+	CStringW GetStrW(int i, bool fSSA = false);
+	CStringW GetStrWA(int i, bool fSSA = false);
+
+#ifdef UNICODE
+#define GetStr GetStrW
+#else
+#define GetStr GetStrA
+#endif
+
+	void SetStr(int i, CStringA str, bool fUnicode /* ignored */);
+	void SetStr(int i, CStringW str, bool fUnicode);
+};
+
+extern BYTE CharSetList[];
+extern TCHAR* CharSetNames[];
+extern int CharSetLen;
+
+class CHtmlColorMap : public CAtlMap<CString, DWORD, CStringElementTraits<CString> > {public: CHtmlColorMap();};
+extern CHtmlColorMap g_colors;
+
+
diff --git a/vsfilter/subtitles/SubtitleInputPin.cpp b/vsfilter/subtitles/SubtitleInputPin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1531af503f2a988836e675d17393e91827d1a9af
--- /dev/null
+++ b/vsfilter/subtitles/SubtitleInputPin.cpp
@@ -0,0 +1,371 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "SubtitleInputPin.h"
+#include "VobSubFile.h"
+#include "RTS.h"
+#include "SSF.h"
+
+#include <initguid.h>
+#include "../include/moreuuids.h"
+
+// our first format id
+#define __GAB1__ "GAB1"
+
+// our tags for __GAB1__ (ushort) + size (ushort)
+
+// "lang" + '0'
+#define __GAB1_LANGUAGE__ 0
+// (int)start+(int)stop+(char*)line+'0'
+#define __GAB1_ENTRY__ 1
+// L"lang" + '0'
+#define __GAB1_LANGUAGE_UNICODE__ 2
+// (int)start+(int)stop+(WCHAR*)line+'0'
+#define __GAB1_ENTRY_UNICODE__ 3
+
+// same as __GAB1__, but the size is (uint) and only __GAB1_LANGUAGE_UNICODE__ is valid
+#define __GAB2__ "GAB2"
+
+// (BYTE*)
+#define __GAB1_RAWTEXTSUBTITLE__ 4
+
+CSubtitleInputPin::CSubtitleInputPin(CBaseFilter* pFilter, CCritSec* pLock, CCritSec* pSubLock, HRESULT* phr)
+	: CBaseInputPin(NAME("CSubtitleInputPin"), pFilter, pLock, phr, L"Input")
+	, m_pSubLock(pSubLock)
+{
+	m_bCanReconnectWhenActive = TRUE;
+}
+
+HRESULT CSubtitleInputPin::CheckMediaType(const CMediaType* pmt)
+{
+	return pmt->majortype == MEDIATYPE_Text && (pmt->subtype == MEDIASUBTYPE_NULL || pmt->subtype == FOURCCMap((DWORD)0))
+		|| pmt->majortype == MEDIATYPE_Subtitle && pmt->subtype == MEDIASUBTYPE_UTF8
+		|| pmt->majortype == MEDIATYPE_Subtitle && (pmt->subtype == MEDIASUBTYPE_SSA || pmt->subtype == MEDIASUBTYPE_ASS || pmt->subtype == MEDIASUBTYPE_ASS2)
+		|| pmt->majortype == MEDIATYPE_Subtitle && pmt->subtype == MEDIASUBTYPE_SSF
+		|| pmt->majortype == MEDIATYPE_Subtitle && (pmt->subtype == MEDIASUBTYPE_VOBSUB)
+		? S_OK 
+		: E_FAIL;
+}
+
+HRESULT CSubtitleInputPin::CompleteConnect(IPin* pReceivePin)
+{
+	if(m_mt.majortype == MEDIATYPE_Text)
+	{
+		if(!(m_pSubStream = new CRenderedTextSubtitle(m_pSubLock))) return E_FAIL;
+		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+		pRTS->m_name = CString(GetPinName(pReceivePin)) + _T(" (embeded)");
+		pRTS->m_dstScreenSize = CSize(384, 288);
+		pRTS->CreateDefaultStyle(DEFAULT_CHARSET);
+	}
+	else if(m_mt.majortype == MEDIATYPE_Subtitle)
+	{
+		SUBTITLEINFO* psi = (SUBTITLEINFO*)m_mt.pbFormat;
+		DWORD dwOffset = psi->dwOffset;
+
+		CString name = ISO6392ToLanguage(psi->IsoLang);
+		if(name.IsEmpty()) name = _T("English");
+		if(wcslen(psi->TrackName) > 0) name += _T(" (") + CString(psi->TrackName) + _T(")");
+
+		if(m_mt.subtype == MEDIASUBTYPE_UTF8 
+		/*|| m_mt.subtype == MEDIASUBTYPE_USF*/
+		|| m_mt.subtype == MEDIASUBTYPE_SSA 
+		|| m_mt.subtype == MEDIASUBTYPE_ASS 
+		|| m_mt.subtype == MEDIASUBTYPE_ASS2)
+		{
+			if(!(m_pSubStream = new CRenderedTextSubtitle(m_pSubLock))) return E_FAIL;
+			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+			pRTS->m_name = name;
+			pRTS->m_dstScreenSize = CSize(384, 288);
+			pRTS->CreateDefaultStyle(DEFAULT_CHARSET);
+
+			if(dwOffset > 0 && m_mt.cbFormat - dwOffset > 0)
+			{
+				CMediaType mt = m_mt;
+				if(mt.pbFormat[dwOffset+0] != 0xef
+				&& mt.pbFormat[dwOffset+1] != 0xbb
+				&& mt.pbFormat[dwOffset+2] != 0xfb)
+				{
+					dwOffset -= 3;
+					mt.pbFormat[dwOffset+0] = 0xef;
+					mt.pbFormat[dwOffset+1] = 0xbb;
+					mt.pbFormat[dwOffset+2] = 0xbf;
+				}
+
+				pRTS->Open(mt.pbFormat + dwOffset, mt.cbFormat - dwOffset, DEFAULT_CHARSET, pRTS->m_name);
+			}
+
+		}
+		else if(m_mt.subtype == MEDIASUBTYPE_SSF)
+		{
+			if(!(m_pSubStream = new ssf::CRenderer(m_pSubLock))) return E_FAIL;
+			ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;
+			pSSF->Open(ssf::MemoryInputStream(m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset, false, false), name);
+		}
+		else if(m_mt.subtype == MEDIASUBTYPE_VOBSUB)
+		{
+			if(!(m_pSubStream = new CVobSubStream(m_pSubLock))) return E_FAIL;
+			CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
+			pVSS->Open(name, m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset);
+		}
+	}
+
+	AddSubStream(m_pSubStream);
+
+    return __super::CompleteConnect(pReceivePin);
+}
+
+HRESULT CSubtitleInputPin::BreakConnect()
+{
+	RemoveSubStream(m_pSubStream);
+	m_pSubStream = NULL;
+
+	ASSERT(IsStopped());
+
+    return __super::BreakConnect();
+}
+
+STDMETHODIMP CSubtitleInputPin::ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt)
+{
+	if(m_Connected)
+	{
+		RemoveSubStream(m_pSubStream);
+		m_pSubStream = NULL;
+
+        m_Connected->Release();
+        m_Connected = NULL;
+	}
+
+	return __super::ReceiveConnection(pConnector, pmt);
+}
+
+STDMETHODIMP CSubtitleInputPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
+{
+	CAutoLock cAutoLock(&m_csReceive);
+
+	if(m_mt.majortype == MEDIATYPE_Text
+	|| m_mt.majortype == MEDIATYPE_Subtitle 
+		&& (m_mt.subtype == MEDIASUBTYPE_UTF8 
+		/*|| m_mt.subtype == MEDIASUBTYPE_USF*/
+		|| m_mt.subtype == MEDIASUBTYPE_SSA 
+		|| m_mt.subtype == MEDIASUBTYPE_ASS 
+		|| m_mt.subtype == MEDIASUBTYPE_ASS2))
+	{
+		CAutoLock cAutoLock(m_pSubLock);
+		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+		pRTS->RemoveAll();
+		pRTS->CreateSegments();
+	}
+	else if(m_mt.majortype == MEDIATYPE_Subtitle && m_mt.subtype == MEDIASUBTYPE_SSF)
+	{
+		CAutoLock cAutoLock(m_pSubLock);
+		ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;
+		// LAME, implement RemoveSubtitles
+		DWORD dwOffset = ((SUBTITLEINFO*)m_mt.pbFormat)->dwOffset;
+		pSSF->Open(ssf::MemoryInputStream(m_mt.pbFormat + dwOffset, m_mt.cbFormat - dwOffset, false, false), _T(""));
+		// pSSF->RemoveSubtitles();
+	}
+	else if(m_mt.majortype == MEDIATYPE_Subtitle && (m_mt.subtype == MEDIASUBTYPE_VOBSUB))
+	{
+		CAutoLock cAutoLock(m_pSubLock);
+		CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
+		pVSS->RemoveAll();
+	}
+
+	return __super::NewSegment(tStart, tStop, dRate);
+}
+
+STDMETHODIMP CSubtitleInputPin::Receive(IMediaSample* pSample)
+{
+	HRESULT hr;
+
+	hr = __super::Receive(pSample);
+    if(FAILED(hr)) return hr;
+
+	CAutoLock cAutoLock(&m_csReceive);
+
+	REFERENCE_TIME tStart, tStop;
+    pSample->GetTime(&tStart, &tStop);
+	tStart += m_tStart; 
+	tStop += m_tStart;
+
+	BYTE* pData = NULL;
+    hr = pSample->GetPointer(&pData);
+    if(FAILED(hr) || pData == NULL) return hr;
+
+	int len = pSample->GetActualDataLength();
+
+	bool fInvalidate = false;
+
+	if(m_mt.majortype == MEDIATYPE_Text)
+	{
+		CAutoLock cAutoLock(m_pSubLock);
+		CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+
+		if(!strncmp((char*)pData, __GAB1__, strlen(__GAB1__)))
+		{
+			char* ptr = (char*)&pData[strlen(__GAB1__)+1];
+			char* end = (char*)&pData[len];
+
+			while(ptr < end)
+			{
+				WORD tag = *((WORD*)(ptr)); ptr += 2;
+				WORD size = *((WORD*)(ptr)); ptr += 2;
+
+				if(tag == __GAB1_LANGUAGE__)
+				{
+					pRTS->m_name = CString(ptr);
+				}
+				else if(tag == __GAB1_ENTRY__)
+				{
+					pRTS->Add(AToW(&ptr[8]), false, *(int*)ptr, *(int*)(ptr+4));
+					fInvalidate = true;
+				}
+				else if(tag == __GAB1_LANGUAGE_UNICODE__)
+				{
+					pRTS->m_name = (WCHAR*)ptr;
+				}
+				else if(tag == __GAB1_ENTRY_UNICODE__)
+				{
+					pRTS->Add((WCHAR*)(ptr+8), true, *(int*)ptr, *(int*)(ptr+4));
+					fInvalidate = true;
+				}
+
+				ptr += size;
+			}
+		}
+		else if(!strncmp((char*)pData, __GAB2__, strlen(__GAB2__)))
+		{
+			char* ptr = (char*)&pData[strlen(__GAB2__)+1];
+			char* end = (char*)&pData[len];
+
+			while(ptr < end)
+			{
+				WORD tag = *((WORD*)(ptr)); ptr += 2;
+				DWORD size = *((DWORD*)(ptr)); ptr += 4;
+
+				if(tag == __GAB1_LANGUAGE_UNICODE__)
+				{
+					pRTS->m_name = (WCHAR*)ptr;
+				}
+				else if(tag == __GAB1_RAWTEXTSUBTITLE__)
+				{
+					pRTS->Open((BYTE*)ptr, size, DEFAULT_CHARSET, pRTS->m_name);
+					fInvalidate = true;
+				}
+
+				ptr += size;
+			}
+		}
+		else if(pData != 0 && len > 1 && *pData != 0)
+		{
+			CStringA str((char*)pData, len);
+
+			str.Replace("\r\n", "\n");
+			str.Trim();
+
+			if(!str.IsEmpty())
+			{
+				pRTS->Add(AToW(str), false, (int)(tStart / 10000), (int)(tStop / 10000));
+				fInvalidate = true;
+			}
+		}
+	}
+	else if(m_mt.majortype == MEDIATYPE_Subtitle)
+	{
+		CAutoLock cAutoLock(m_pSubLock);
+
+		if(m_mt.subtype == MEDIASUBTYPE_UTF8)
+		{
+			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+
+			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
+			if(!str.IsEmpty())
+			{
+				pRTS->Add(str, true, (int)(tStart / 10000), (int)(tStop / 10000));
+				fInvalidate = true;
+			}
+		}
+		else if(m_mt.subtype == MEDIASUBTYPE_SSA || m_mt.subtype == MEDIASUBTYPE_ASS || m_mt.subtype == MEDIASUBTYPE_ASS2)
+		{
+			CRenderedTextSubtitle* pRTS = (CRenderedTextSubtitle*)(ISubStream*)m_pSubStream;
+
+			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
+			if(!str.IsEmpty())
+			{
+				STSEntry stse;
+
+				int fields = m_mt.subtype == MEDIASUBTYPE_ASS2 ? 10 : 9;
+
+				CAtlList<CStringW> sl;
+				Explode(str, sl, ',', fields);
+				if(sl.GetCount() == fields)
+				{
+					stse.readorder = wcstol(sl.RemoveHead(), NULL, 10);
+					stse.layer = wcstol(sl.RemoveHead(), NULL, 10);
+					stse.style = sl.RemoveHead();
+					stse.actor = sl.RemoveHead();
+					stse.marginRect.left = wcstol(sl.RemoveHead(), NULL, 10);
+					stse.marginRect.right = wcstol(sl.RemoveHead(), NULL, 10);
+					stse.marginRect.top = stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10);
+					if(fields == 10) stse.marginRect.bottom = wcstol(sl.RemoveHead(), NULL, 10);
+					stse.effect = sl.RemoveHead();
+					stse.str = sl.RemoveHead();
+				}
+
+				if(!stse.str.IsEmpty())
+				{
+					pRTS->Add(stse.str, true, (int)(tStart / 10000), (int)(tStop / 10000), 
+						stse.style, stse.actor, stse.effect, stse.marginRect, stse.layer, stse.readorder);
+					fInvalidate = true;
+				}
+			}
+		}
+		else if(m_mt.subtype == MEDIASUBTYPE_SSF)
+		{
+			ssf::CRenderer* pSSF = (ssf::CRenderer*)(ISubStream*)m_pSubStream;
+
+			CStringW str = UTF8To16(CStringA((LPCSTR)pData, len)).Trim();
+			if(!str.IsEmpty())
+			{
+				pSSF->Append(tStart, tStop, str);
+				fInvalidate = true;
+			}
+		}
+		else if(m_mt.subtype == MEDIASUBTYPE_VOBSUB)
+		{
+			CVobSubStream* pVSS = (CVobSubStream*)(ISubStream*)m_pSubStream;
+			pVSS->Add(tStart, tStop, pData, len);
+		}
+	}
+
+	if(fInvalidate)
+	{
+		TRACE(_T("InvalidateSubtitle(%I64d, ..)\n"), tStart);
+		// IMPORTANT: m_pSubLock must not be locked when calling this
+		InvalidateSubtitle(tStart, m_pSubStream);
+	}
+
+	hr = S_OK;
+
+    return hr;
+}
+
diff --git a/vsfilter/subtitles/SubtitleInputPin.h b/vsfilter/subtitles/SubtitleInputPin.h
new file mode 100644
index 0000000000000000000000000000000000000000..976a421e9451c016d3ebf1f3d7a2f69ac8b7e983
--- /dev/null
+++ b/vsfilter/subtitles/SubtitleInputPin.h
@@ -0,0 +1,53 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include "..\subpic\ISubPic.h"
+
+//
+// CSubtitleInputPin
+//
+
+class CSubtitleInputPin : public CBaseInputPin
+{
+	CCritSec m_csReceive;
+
+	CCritSec* m_pSubLock;
+	CComPtr<ISubStream> m_pSubStream;
+
+protected:
+	virtual void AddSubStream(ISubStream* pSubStream) = 0;
+	virtual void RemoveSubStream(ISubStream* pSubStream) = 0;
+	virtual void InvalidateSubtitle(REFERENCE_TIME rtStart, ISubStream* pSubStream) = 0;
+
+public:
+	CSubtitleInputPin(CBaseFilter* pFilter, CCritSec* pLock, CCritSec* pSubLock, HRESULT* phr);
+
+	HRESULT CheckMediaType(const CMediaType* pmt);
+	HRESULT CompleteConnect(IPin* pReceivePin);
+	HRESULT BreakConnect();
+	STDMETHODIMP ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt);
+	STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
+	STDMETHODIMP Receive(IMediaSample* pSample);
+
+	ISubStream* GetSubStream() {return m_pSubStream;}
+};
diff --git a/vsfilter/subtitles/TextFile.cpp b/vsfilter/subtitles/TextFile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8627715abdf6f4905d6ce25ee257ac9cb0603115
--- /dev/null
+++ b/vsfilter/subtitles/TextFile.cpp
@@ -0,0 +1,537 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <atlbase.h>
+#include <afxinet.h>
+#include "TextFile.h"
+
+CTextFile::CTextFile(enc e)
+{
+	m_encoding = m_defaultencoding = e;
+	m_offset = 0;
+}
+
+bool CTextFile::Open(LPCTSTR lpszFileName)
+{
+	if(!__super::Open(lpszFileName, modeRead|typeBinary|shareDenyWrite))
+		return(false);
+
+	m_encoding = m_defaultencoding;
+	m_offset = 0;
+
+	if(__super::GetLength() >= 2)
+	{
+		WORD w;
+		if(sizeof(w) != Read(&w, sizeof(w)))
+			return Close(), false;
+
+		if(w == 0xfeff)
+		{
+			m_encoding = LE16;
+			m_offset = 2;
+		}
+		else if(w == 0xfffe)
+		{
+			m_encoding = BE16;
+			m_offset = 2;
+		}
+		else if(w == 0xbbef && __super::GetLength() >= 3)
+		{
+			BYTE b;
+			if(sizeof(b) != Read(&b, sizeof(b)))
+				return Close(), false;
+
+			if(b == 0xbf)
+			{
+				m_encoding = UTF8;
+				m_offset = 3;
+			}
+		}
+	}
+
+	if(m_encoding == m_defaultencoding)
+	{
+		__super::Close(); // CWebTextFile::Close() would delete the temp file if we called it...
+		if(!__super::Open(lpszFileName, modeRead|typeText|shareDenyWrite))
+			return(false);
+	}
+
+	return(true);
+}
+
+bool CTextFile::Save(LPCTSTR lpszFileName, enc e)
+{
+	if(!__super::Open(lpszFileName, modeCreate|modeWrite|shareDenyWrite|(e==ASCII?typeText:typeBinary)))
+		return(false);
+
+	if(e == UTF8)
+	{
+		BYTE b[3] = {0xef,0xbb,0xbf};
+		Write(b, sizeof(b));
+	}
+	else if(e == LE16)
+	{
+		BYTE b[2] = {0xff,0xfe};
+		Write(b, sizeof(b));
+	}
+	else if(e == BE16)
+	{
+		BYTE b[2] = {0xfe,0xff};
+		Write(b, sizeof(b));
+	}
+
+	m_encoding = e;
+
+	return true;
+}
+
+void CTextFile::SetEncoding(enc e)
+{
+	m_encoding = e;
+}
+
+CTextFile::enc CTextFile::GetEncoding()
+{
+	return m_encoding;
+}
+
+bool CTextFile::IsUnicode()
+{
+	return m_encoding == UTF8 || m_encoding == LE16 || m_encoding == BE16;
+}
+
+// CFile
+
+CString CTextFile::GetFilePath() const
+{
+	// to avoid a CException coming from CTime
+	return m_strFileName; // __super::GetFilePath();
+}
+
+// CStdioFile
+
+ULONGLONG CTextFile::GetPosition() const
+{
+	return(CStdioFile::GetPosition() - m_offset);
+}
+
+ULONGLONG CTextFile::GetLength() const
+{
+	return(CStdioFile::GetLength() - m_offset);
+}
+
+ULONGLONG CTextFile::Seek(LONGLONG lOff, UINT nFrom)
+{
+	ULONGLONG pos = GetPosition();
+	ULONGLONG len = GetLength();
+
+	switch(nFrom)
+	{
+	default:
+	case begin: lOff = lOff; break;
+	case current: lOff = pos + lOff; break;
+	case end: lOff = len - lOff; break;
+	}
+
+	lOff = max(min(lOff, len), 0) + m_offset;
+
+	pos = CStdioFile::Seek(lOff, begin) - m_offset;
+
+	return(pos);
+}
+
+void CTextFile::WriteString(LPCSTR lpsz/*CStringA str*/)
+{
+	CStringA str(lpsz);
+
+	if(m_encoding == ASCII)
+	{
+		__super::WriteString(AToT(str));
+	}
+	else if(m_encoding == ANSI)
+	{
+		str.Replace("\n", "\r\n");
+		Write((LPCSTR)str, str.GetLength());
+	}
+	else if(m_encoding == UTF8)
+	{
+		WriteString(AToW(str));
+	}
+	else if(m_encoding == LE16)
+	{
+		WriteString(AToW(str));
+	}
+	else if(m_encoding == BE16)
+	{
+		WriteString(AToW(str));
+	}
+}
+
+void CTextFile::WriteString(LPCWSTR lpsz/*CStringW str*/)
+{
+	CStringW str(lpsz);
+
+	if(m_encoding == ASCII)
+	{
+		__super::WriteString(WToT(str));
+	}
+	else if(m_encoding == ANSI)
+	{
+		str.Replace(L"\n", L"\r\n");
+		CStringA stra = CStringA(CString(str)); // TODO: codepage
+		Write((LPCSTR)stra, stra.GetLength());
+	}
+	else if(m_encoding == UTF8)
+	{
+		str.Replace(L"\n", L"\r\n");
+		for(int i = 0; i < str.GetLength(); i++)
+		{
+			DWORD c = (WORD)str[i];
+
+			if(0 <= c && c < 0x80) // 0xxxxxxx
+			{
+				Write(&c, 1);
+			}
+			else if(0x80 <= c && c < 0x800) // 110xxxxx 10xxxxxx
+			{
+				c = 0xc080|((c<<2)&0x1f00)|(c&0x003f);
+				Write((BYTE*)&c+1, 1);
+				Write(&c, 1);
+			}
+			else if(0x800 <= c && c < 0xFFFF) // 1110xxxx 10xxxxxx 10xxxxxx
+			{
+				c = 0xe08080|((c<<4)&0x0f0000)|((c<<2)&0x3f00)|(c&0x003f);
+				Write((BYTE*)&c+2, 1);
+				Write((BYTE*)&c+1, 1);
+				Write(&c, 1);
+			}
+			else
+			{
+				c = '?';
+				Write(&c, 1);
+			}
+		}
+	}
+	else if(m_encoding == LE16)
+	{
+		str.Replace(L"\n", L"\r\n");
+		Write((LPCWSTR)str, str.GetLength()*2);
+	}
+	else if(m_encoding == BE16)
+	{
+		str.Replace(L"\n", L"\r\n");
+		for(int i = 0; i < str.GetLength(); i++)
+			str.SetAt(i, ((str[i]>>8)&0x00ff)|((str[i]<<8)&0xff00));
+		Write((LPCWSTR)str, str.GetLength()*2);
+	}
+}
+
+BOOL CTextFile::ReadString(CStringA& str)
+{
+	bool fEOF = true;
+
+	str.Empty();
+
+	if(m_encoding == ASCII)
+	{
+		CString s;
+		fEOF = !__super::ReadString(s);
+		str = TToA(s);
+	}
+	else if(m_encoding == ANSI)
+	{
+		char c;
+		while(Read(&c, sizeof(c)) == sizeof(c))
+		{
+			fEOF = false;
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			str += c;
+		}
+	}
+	else if(m_encoding == UTF8)
+	{
+		BYTE b;
+		while(Read(&b, sizeof(b)) == sizeof(b))
+		{
+			fEOF = false;
+			char c = '?';
+			if(!(b&0x80)) // 0xxxxxxx
+			{
+				c = b&0x7f;
+			}
+			else if((b&0xe0) == 0xc0) // 110xxxxx 10xxxxxx
+			{
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+			}
+			else if((b&0xf0) == 0xe0) // 1110xxxx 10xxxxxx 10xxxxxx
+			{
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+			}
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			str += c;
+		}
+	}
+	else if(m_encoding == LE16)
+	{
+		WORD w;
+		while(Read(&w, sizeof(w)) == sizeof(w))
+		{
+			fEOF = false;
+			char c = '?';
+			if(!(w&0xff00)) c = w&0xff;
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			str += c;
+		}
+	}
+	else if(m_encoding == BE16)
+	{
+		WORD w;
+		while(Read(&w, sizeof(w)) == sizeof(w))
+		{
+			fEOF = false;
+			char c = '?';
+			if(!(w&0xff)) c = w>>8;
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			str += c;
+		}
+	}
+
+	return(!fEOF);
+}
+
+BOOL CTextFile::ReadString(CStringW& str)
+{
+	bool fEOF = true;
+
+	str.Empty();
+
+	if(m_encoding == ASCII)
+	{
+		CString s;
+		fEOF = !__super::ReadString(s);
+		str = TToW(s);
+	}
+	else if(m_encoding == ANSI)
+	{
+		CStringA stra;
+		char c;
+		while(Read(&c, sizeof(c)) == sizeof(c))
+		{
+			fEOF = false;
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			stra += c;
+		}
+		str = CStringW(CString(stra)); // TODO: codepage
+	}
+	else if(m_encoding == UTF8)
+	{
+		BYTE b;
+		while(Read(&b, sizeof(b)) == sizeof(b))
+		{
+			fEOF = false;
+			WCHAR c = '?';
+			if(!(b&0x80)) // 0xxxxxxx
+			{
+				c = b&0x7f;
+			}
+			else if((b&0xe0) == 0xc0) // 110xxxxx 10xxxxxx
+			{
+				c = (b&0x1f)<<6;
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+				c |= (b&0x3f);
+			}
+			else if((b&0xf0) == 0xe0) // 1110xxxx 10xxxxxx 10xxxxxx
+			{
+				c = (b&0x0f)<<12;
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+				c |= (b&0x3f)<<6;
+				if(Read(&b, sizeof(b)) != sizeof(b)) break;
+				c |= (b&0x3f);
+			}
+			if(c == '\r') continue;
+			if(c == '\n') break;
+			str += c;
+		}
+	}
+	else if(m_encoding == LE16)
+	{
+		WCHAR wc;
+		while(Read(&wc, sizeof(wc)) == sizeof(wc))
+		{
+			fEOF = false;
+			if(wc == '\r') continue;
+			if(wc == '\n') break;
+			str += wc;
+		}
+	}
+	else if(m_encoding == BE16)
+	{
+		WCHAR wc;
+		while(Read(&wc, sizeof(wc)) == sizeof(wc))
+		{
+			fEOF = false;
+			wc = ((wc>>8)&0x00ff)|((wc<<8)&0xff00);
+			if(wc == '\r') continue;
+			if(wc == '\n') break;
+			str += wc;
+		}
+	}
+
+	return(!fEOF);
+}
+
+//
+// CWebTextFile
+//
+
+CWebTextFile::CWebTextFile(LONGLONG llMaxSize)
+	: m_llMaxSize(llMaxSize)
+{
+}
+
+bool CWebTextFile::Open(LPCTSTR lpszFileName)
+{
+	CString fn(lpszFileName);
+
+	if(fn.Find(_T("http://")) != 0)
+		return __super::Open(lpszFileName);
+
+	try
+	{
+		CInternetSession is;
+
+		CAutoPtr<CStdioFile> f(is.OpenURL(fn, 1, INTERNET_FLAG_TRANSFER_BINARY|INTERNET_FLAG_EXISTING_CONNECT));
+		if(!f) return(false);
+
+		TCHAR path[MAX_PATH];
+		GetTempPath(MAX_PATH, path);
+
+		fn = path + fn.Mid(fn.ReverseFind('/')+1);
+		int i = fn.Find(_T("?"));
+		if(i > 0) fn = fn.Left(i);
+		CFile temp;
+		if(!temp.Open(fn, modeCreate|modeWrite|typeBinary|shareDenyWrite))
+		{
+			f->Close();
+			return(false);
+		}
+
+		BYTE buff[1024];
+		int len, total = 0;
+		while((len = f->Read(buff, 1024)) == 1024 && (m_llMaxSize < 0 || (total+=1024) < m_llMaxSize))
+			temp.Write(buff, len);
+		if(len > 0) temp.Write(buff, len);
+
+		m_tempfn = fn;
+
+		f->Close(); // must close it because the desctructor doesn't seem to do it and we will get an exception when "is" is destroying
+	}
+	catch(CInternetException* ie)
+	{
+		ie->Delete();
+		return(false);
+	}
+
+	return __super::Open(m_tempfn);
+}
+
+bool CWebTextFile::Save(LPCTSTR lpszFileName, enc e)
+{
+	// CWebTextFile is read-only...
+	ASSERT(0);
+	return(false);
+}
+
+void CWebTextFile::Close()
+{
+	__super::Close();
+
+	if(!m_tempfn.IsEmpty())
+	{
+		_tremove(m_tempfn);
+		m_tempfn.Empty();
+	}
+}
+
+///////////////////////////////////////////////////////////////
+
+CStringW AToW(CStringA str)
+{
+	CStringW ret;
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (WCHAR)(BYTE)str[i];
+	return(ret);
+}
+
+CStringA WToA(CStringW str)
+{
+	CStringA ret;
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (CHAR)(WORD)str[i];
+	return(ret);
+}
+
+CString AToT(CStringA str)
+{
+	CString ret;
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (TCHAR)(BYTE)str[i];
+	return(ret);
+}
+
+CString WToT(CStringW str)
+{
+	CString ret;
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (TCHAR)(WORD)str[i];
+	return(ret);
+}
+
+CStringA TToA(CString str)
+{
+	CStringA ret;
+#ifdef UNICODE
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (CHAR)(BYTE)str[i];
+#else
+	ret = str;
+#endif
+	return(ret);
+}
+
+CStringW TToW(CString str)
+{
+	CStringW ret;
+#ifdef UNICODE
+	ret = str;
+#else
+	for(int i = 0, j = str.GetLength(); i < j; i++)
+		ret += (WCHAR)(BYTE)str[i];
+#endif
+	return(ret);
+}
diff --git a/vsfilter/subtitles/TextFile.h b/vsfilter/subtitles/TextFile.h
new file mode 100644
index 0000000000000000000000000000000000000000..d527d792d44ea8b5a41e363f62f4106549f372f8
--- /dev/null
+++ b/vsfilter/subtitles/TextFile.h
@@ -0,0 +1,79 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <afx.h>
+
+class CTextFile : protected CStdioFile
+{
+public:
+	typedef enum {ASCII, UTF8, LE16, BE16, ANSI} enc;
+
+private:
+	enc m_encoding, m_defaultencoding;
+	int m_offset;
+
+public:
+	CTextFile(enc e = ASCII);
+
+	virtual bool Open(LPCTSTR lpszFileName);
+	virtual bool Save(LPCTSTR lpszFileName, enc e /*= ASCII*/);
+
+	void SetEncoding(enc e);
+	enc GetEncoding();
+	bool IsUnicode();
+
+	// CFile
+
+	CString GetFilePath() const;
+
+	// CStdioFile
+
+    ULONGLONG GetPosition() const;
+	ULONGLONG GetLength() const;
+	ULONGLONG Seek(LONGLONG lOff, UINT nFrom);
+
+	void WriteString(LPCSTR lpsz/*CStringA str*/);
+	void WriteString(LPCWSTR lpsz/*CStringW str*/);
+	BOOL ReadString(CStringA& str);
+	BOOL ReadString(CStringW& str);
+};
+
+class CWebTextFile : public CTextFile
+{
+	LONGLONG m_llMaxSize;
+	CString m_tempfn;
+
+public:
+	CWebTextFile(LONGLONG llMaxSize = 1024*1024);
+
+	bool Open(LPCTSTR lpszFileName);
+	bool Save(LPCTSTR lpszFileName, enc e /*= ASCII*/);
+	void Close();
+};
+
+extern CStringW AToW(CStringA str);
+extern CStringA WToA(CStringW str);
+extern CString AToT(CStringA str);
+extern CString WToT(CStringW str);
+extern CStringA TToA(CString str);
+extern CStringW TToW(CString str);
diff --git a/vsfilter/subtitles/USFSubtitles.cpp b/vsfilter/subtitles/USFSubtitles.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4329ac331481f55c8769bd4df3172d2136dc2926
--- /dev/null
+++ b/vsfilter/subtitles/USFSubtitles.cpp
@@ -0,0 +1,772 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "StdAfx.h"
+#include "usfsubtitles.h"
+
+#define DeclareNameAndValue(pNode, name, val) \
+    CComBSTR name; \
+    pNode->get_nodeName(&name); \
+	name.ToLower(); \
+	CComVariant val; \
+    pNode->get_nodeValue(&val); \
+
+#define BeginEnumAttribs(pNode, pChild, name, value) \
+	{CComPtr<IXMLDOMNamedNodeMap> pAttribs; \
+	if(SUCCEEDED(pNode->get_attributes(&pAttribs)) && pAttribs != NULL) \
+    { \
+		CComPtr<IXMLDOMNode> pChild; \
+        for(pAttribs->nextNode(&pChild); pChild; pChild = NULL, pAttribs->nextNode(&pChild)) \
+        { \
+
+#define EndEnumAttribs }}}
+
+#define BeginEnumChildren(pNode, pChild) \
+	{CComPtr<IXMLDOMNode> pChild, pNext; \
+	for(pNode->get_firstChild(&pChild); pChild; pNext = NULL, pChild->get_nextSibling(&pNext), pChild = pNext) \
+    { \
+
+#define EndEnumChildren }}
+
+static CStringW GetText(CComPtr<IXMLDOMNode> pNode)
+{
+	CComBSTR bstr;
+	pNode->get_text(&bstr);
+
+	return(CStringW(bstr));
+}
+
+static CStringW GetXML(CComPtr<IXMLDOMNode> pNode)
+{
+	CComBSTR bstr;
+	pNode->get_xml(&bstr);
+	CStringW str(bstr);
+	str.Remove('\r');
+	str.Replace('\n', ' ');
+	for(int i = 0; (i = str.Find(L" ", i)) >= 0; )
+	{
+		for(++i; i < str.GetLength() && (str[i] == '\n' || str[i] == ' ');)
+			str.Delete(i);
+	}
+	return(str);
+}
+
+static CStringW GetAttrib(CStringW attrib, CComPtr<IXMLDOMNode> pNode)
+{
+	CStringW ret;
+
+	BeginEnumAttribs(pNode, pChild, name, val)
+	{
+		DeclareNameAndValue(pChild, name, val);
+
+		if(CStringW(name) == attrib && val.vt == VT_BSTR) // TODO: prepare for other types
+		{
+			ret = val.bstrVal;
+			break;
+		}
+	}
+	EndEnumAttribs
+
+	return(ret);
+}
+
+static int TimeToInt(CStringW str)
+{
+	CAtlList<CStringW> sl;
+	int i = 0;
+	for(CStringW token = str.Tokenize(L":.,", i); !token.IsEmpty(); token = str.Tokenize(L":.,", i))
+		sl.AddHead(token);
+
+	if(sl.GetCount() > 4)
+		return(-1);
+
+	int time = 0;
+	
+	int mul[4] = {1,1000,60*1000,60*60*1000};
+	POSITION pos = sl.GetHeadPosition();
+	for(i = 0; pos; i++)
+	{
+		const WCHAR* s = sl.GetNext(pos);
+		WCHAR* tmp = NULL;
+		int t = wcstol(s, &tmp, 10);
+		if(s >= tmp) return(-1);
+		time += t * mul[i];
+	}
+
+	return(time);
+}
+
+static DWORD StringToDWORD(CStringW str)
+{
+	if(str.IsEmpty()) return(0);
+	if(str[0] == '#') return((DWORD)wcstol(str, NULL, 16));
+	else return((DWORD)wcstol(str, NULL, 10));	
+}
+
+static DWORD ColorToDWORD(CStringW str)
+{
+	if(str.IsEmpty()) return(0);
+
+	DWORD ret = 0;
+
+	if(str[0] == '#')
+	{
+		ret = (DWORD)wcstol(str.TrimLeft('#'), NULL, 16);
+	}
+	else
+	{
+		g_colors.Lookup(CString(str), ret);
+	}
+
+	ret = ((ret&0xff)<<16)|(ret&0xff00ff00)|((ret>>16)&0xff);
+
+	return(ret);
+}
+
+static int TranslateAlignment(CStringW alignment)
+{
+	return
+		!alignment.CompareNoCase(L"BottomLeft") ? 1 :
+		!alignment.CompareNoCase(L"BottomCenter") ? 2 :
+		!alignment.CompareNoCase(L"BottomRight") ? 3 :
+		!alignment.CompareNoCase(L"MiddleLeft") ? 4 :
+		!alignment.CompareNoCase(L"MiddleCenter") ? 5 :
+		!alignment.CompareNoCase(L"MiddleRight") ? 6 :
+		!alignment.CompareNoCase(L"TopLeft") ? 7 :
+		!alignment.CompareNoCase(L"TopCenter") ? 8 :
+		!alignment.CompareNoCase(L"TopRight") ? 9 :
+		2;
+}
+
+static int TranslateMargin(CStringW margin, int wndsize)
+{
+	int ret = 0;
+
+	if(!margin.IsEmpty())
+	{
+		ret = wcstol(margin, NULL, 10);
+		if(margin.Find('%') >= 0) ret = wndsize * ret / 100;
+	}
+
+	return(ret);
+}
+
+////////////////
+
+CUSFSubtitles::CUSFSubtitles()
+{
+}
+
+CUSFSubtitles::~CUSFSubtitles()
+{
+}
+
+bool CUSFSubtitles::Read(LPCTSTR fn)
+{
+	VARIANT_BOOL vb;
+	CComPtr<IXMLDOMDocument> pDoc;
+	if(FAILED(pDoc.CoCreateInstance(CLSID_DOMDocument))
+	|| FAILED(pDoc->put_async(VARIANT_FALSE))
+	|| FAILED(pDoc->load(CComVariant(fn), &vb)) || vb != VARIANT_TRUE)
+		return(false);
+
+	styles.RemoveAll();
+	effects.RemoveAll();
+	texts.RemoveAll();
+
+	if(!ParseUSFSubtitles(CComQIPtr<IXMLDOMNode>(pDoc)))
+		return(false);
+
+	POSITION pos = styles.GetHeadPosition();
+	while(pos)
+	{
+		style_t* def = styles.GetNext(pos);
+
+		if(def->name.CompareNoCase(L"Default"))
+			continue;
+
+		POSITION pos2 = styles.GetHeadPosition();
+		while(pos2)
+		{
+			style_t* s = styles.GetNext(pos2);
+
+			if(!s->name.CompareNoCase(L"Default"))
+				continue;
+
+			if(s->fontstyle.face.IsEmpty()) s->fontstyle.face = def->fontstyle.face;
+			if(s->fontstyle.size.IsEmpty()) s->fontstyle.size = def->fontstyle.size;
+			if(s->fontstyle.color[0].IsEmpty()) s->fontstyle.color[0] = def->fontstyle.color[0];
+			if(s->fontstyle.color[1].IsEmpty()) s->fontstyle.color[1] = def->fontstyle.color[1];
+			if(s->fontstyle.color[2].IsEmpty()) s->fontstyle.color[2] = def->fontstyle.color[2];
+			if(s->fontstyle.color[3].IsEmpty()) s->fontstyle.color[3] = def->fontstyle.color[3];
+			if(s->fontstyle.italic.IsEmpty()) s->fontstyle.italic = def->fontstyle.italic;
+			if(s->fontstyle.weight.IsEmpty()) s->fontstyle.weight = def->fontstyle.weight;
+			if(s->fontstyle.underline.IsEmpty()) s->fontstyle.underline = def->fontstyle.underline;
+			if(s->fontstyle.alpha.IsEmpty()) s->fontstyle.alpha = def->fontstyle.alpha;
+			if(s->fontstyle.outline.IsEmpty()) s->fontstyle.outline = def->fontstyle.outline;
+			if(s->fontstyle.shadow.IsEmpty()) s->fontstyle.shadow = def->fontstyle.shadow;
+			if(s->fontstyle.wrap.IsEmpty()) s->fontstyle.wrap = def->fontstyle.wrap;
+			if(s->pal.alignment.IsEmpty()) s->pal.alignment = def->pal.alignment;
+			if(s->pal.relativeto.IsEmpty()) s->pal.relativeto = def->pal.relativeto;
+			if(s->pal.horizontal_margin.IsEmpty()) s->pal.horizontal_margin = def->pal.horizontal_margin;
+			if(s->pal.vertical_margin.IsEmpty()) s->pal.vertical_margin = def->pal.vertical_margin;
+			if(s->pal.rotate[0].IsEmpty()) s->pal.rotate[0] = def->pal.rotate[0];
+			if(s->pal.rotate[1].IsEmpty()) s->pal.rotate[1] = def->pal.rotate[1];
+			if(s->pal.rotate[2].IsEmpty()) s->pal.rotate[2] = def->pal.rotate[2];
+		}
+
+		break;
+	}
+
+	pos = texts.GetHeadPosition();
+	while(pos)
+	{
+		text_t* t = texts.GetNext(pos);
+		if(t->style.IsEmpty()) t->style = L"Default";
+	}
+
+	return(true);
+}
+
+bool CUSFSubtitles::ConvertToSTS(CSimpleTextSubtitle& sts)
+{
+	sts.m_name = metadata.language.text;
+	sts.m_encoding = CTextFile::UTF8;
+	sts.m_dstScreenSize = CSize(640, 480);
+	sts.m_fScaledBAS = true;
+	sts.m_defaultWrapStyle = 1;
+
+	// TODO: map metadata.language.code to charset num (windows doesn't have such a function...)
+	int charSet = DEFAULT_CHARSET; 
+
+	POSITION pos = styles.GetHeadPosition();
+	while(pos)
+	{
+		style_t* s = styles.GetNext(pos);
+
+		if(!s->name.CompareNoCase(L"Default") && !s->fontstyle.wrap.IsEmpty())
+		{
+			sts.m_defaultWrapStyle = 
+				!s->fontstyle.wrap.CompareNoCase(L"no") ? 2 :
+				!s->fontstyle.wrap.CompareNoCase(L"auto") ? 1 :
+				1;
+		}
+
+		STSStyle* stss = new STSStyle;
+		if(!stss) continue;
+
+		if(!s->pal.horizontal_margin.IsEmpty())
+			stss->marginRect.left = stss->marginRect.right = TranslateMargin(s->pal.horizontal_margin, sts.m_dstScreenSize.cx);
+		if(!s->pal.vertical_margin.IsEmpty())
+			stss->marginRect.top = stss->marginRect.bottom = TranslateMargin(s->pal.vertical_margin, sts.m_dstScreenSize.cy);
+
+		stss->scrAlignment = TranslateAlignment(s->pal.alignment);
+
+		if(!s->pal.relativeto.IsEmpty()) stss->relativeTo = 
+			!s->pal.relativeto.CompareNoCase(L"window") ? 0 :
+			!s->pal.relativeto.CompareNoCase(L"video") ? 1 :
+			0;
+
+		stss->borderStyle = 0;
+		if(!s->fontstyle.outline.IsEmpty()) stss->outlineWidth = wcstol(s->fontstyle.outline, NULL, 10);
+		if(!s->fontstyle.shadow.IsEmpty()) stss->shadowDepth = wcstol(s->fontstyle.shadow, NULL, 10);
+
+		for(int i = 0; i < 4; i++)
+		{
+			DWORD color = ColorToDWORD(s->fontstyle.color[i]);
+			int alpha = (BYTE)wcstol(s->fontstyle.alpha, NULL, 10);
+
+			stss->colors[i] = color & 0xffffff;
+			stss->alpha[i] = (BYTE)(color>>24);
+
+			stss->alpha[i] = stss->alpha[i] + (255 - stss->alpha[i]) * min(max(alpha, 0), 100) / 100;
+		}
+
+		if(!s->fontstyle.face.IsEmpty()) stss->fontName = s->fontstyle.face;
+		if(!s->fontstyle.size.IsEmpty()) stss->fontSize = wcstol(s->fontstyle.size, NULL, 10);
+		if(!s->fontstyle.weight.IsEmpty()) stss->fontWeight = 
+			!s->fontstyle.weight.CompareNoCase(L"normal") ? FW_NORMAL :
+			!s->fontstyle.weight.CompareNoCase(L"bold") ? FW_BOLD :
+			!s->fontstyle.weight.CompareNoCase(L"lighter") ? FW_LIGHT :
+			!s->fontstyle.weight.CompareNoCase(L"bolder") ? FW_SEMIBOLD :
+			wcstol(s->fontstyle.weight, NULL, 10);
+		if(stss->fontWeight == 0) stss->fontWeight = FW_NORMAL;
+		if(!s->fontstyle.italic.IsEmpty()) stss->fItalic = s->fontstyle.italic.CompareNoCase(L"yes") == 0;
+		if(!s->fontstyle.underline.IsEmpty()) stss->fUnderline = s->fontstyle.underline.CompareNoCase(L"yes") == 0;
+		if(!s->pal.rotate[0].IsEmpty()) stss->fontAngleZ = wcstol(s->pal.rotate[0], NULL, 10);
+		if(!s->pal.rotate[1].IsEmpty()) stss->fontAngleX = wcstol(s->pal.rotate[1], NULL, 10);
+		if(!s->pal.rotate[2].IsEmpty()) stss->fontAngleY = wcstol(s->pal.rotate[2], NULL, 10);
+
+		stss->charSet = charSet;
+
+		sts.AddStyle(WToT(s->name), stss);
+	}
+
+	pos = texts.GetHeadPosition();
+	while(pos)
+	{
+		text_t* t = texts.GetNext(pos);
+
+		if(!t->pal.alignment.IsEmpty())
+		{
+			CStringW s;
+			s.Format(L"{\\an%d}", TranslateAlignment(t->pal.alignment));
+			t->str = s + t->str;
+		}
+
+		CRect marginRect;
+		marginRect.SetRectEmpty();
+
+		if(!t->pal.horizontal_margin.IsEmpty())
+			marginRect.left = marginRect.right = TranslateMargin(t->pal.horizontal_margin, sts.m_dstScreenSize.cx);
+		if(!t->pal.vertical_margin.IsEmpty())
+			marginRect.top = marginRect.bottom = TranslateMargin(t->pal.vertical_margin, sts.m_dstScreenSize.cy);
+
+		WCHAR rtags[3][8] = {L"{\\rz%d}", L"{\\rx%d}", L"{\\ry%d}"};
+		for(int i = 0; i < 3; i++)
+		{
+			if(int angle = wcstol(t->pal.rotate[i], NULL, 10))
+			{
+				CStringW str;
+				str.Format(rtags[i], angle);
+				t->str = str + t->str;
+			}
+		}
+
+		if(t->style.CompareNoCase(L"Default") != 0)
+		{
+			POSITION pos = styles.GetHeadPosition();
+			while(pos)
+			{
+				style_t* s = styles.GetNext(pos);
+				if(s->name == t->style && !s->fontstyle.wrap.IsEmpty())
+				{
+					int WrapStyle = 
+						!s->fontstyle.wrap.CompareNoCase(L"no") ? 2 :
+						!s->fontstyle.wrap.CompareNoCase(L"auto") ? 1 :
+						1;
+
+					if(WrapStyle != sts.m_defaultWrapStyle)
+					{
+						CStringW str;
+                        str.Format(L"{\\q%d}", WrapStyle);
+						t->str = str + t->str;
+					}
+
+					break;
+				}
+			}
+		}
+
+		// TODO: apply effects as {\t(..)} after usf's standard clearly defines them
+
+		sts.Add(t->str, true, t->start, t->stop, WToT(t->style), _T(""), _T(""), marginRect);
+	}
+
+	return(true);
+}
+
+bool CUSFSubtitles::ParseUSFSubtitles(CComPtr<IXMLDOMNode> pNode)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"usfsubtitles")
+	{
+		// metadata
+
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"metadata")
+			{
+				ParseMetadata(pChild, metadata);
+			}
+		}
+		EndEnumChildren
+
+		// styles
+
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"styles")
+			{
+				BeginEnumChildren(pChild, pGrandChild) // :)
+				{
+					DeclareNameAndValue(pGrandChild, name, val);
+
+					if(name == L"style")
+					{
+						CAutoPtr<style_t> s(new style_t);
+						if(s)
+						{
+							ParseStyle(pGrandChild, s);
+							styles.AddTail(s);
+						}
+					}
+				}
+				EndEnumChildren
+			}
+		}
+		EndEnumChildren
+
+		// effects
+
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"effects")
+			{
+				BeginEnumChildren(pChild, pGrandChild) // :)
+				{
+					DeclareNameAndValue(pGrandChild, name, val);
+
+					if(name == L"effect")
+					{
+						CAutoPtr<effect_t> e(new effect_t);
+						if(e)
+						{
+							ParseEffect(pGrandChild, e);
+							effects.AddTail(e);
+						}
+					}
+				}
+				EndEnumChildren
+			}
+		}
+		EndEnumChildren
+
+		// subtitles
+
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"subtitles")
+			{
+				BeginEnumChildren(pChild, pGrandChild) // :)
+				{
+					DeclareNameAndValue(pGrandChild, name, val);
+
+					if(name == L"subtitle")
+					{
+						CStringW sstart = GetAttrib(L"start", pGrandChild);
+						CStringW sstop = GetAttrib(L"stop", pGrandChild);
+						CStringW sduration = GetAttrib(L"duration", pGrandChild);
+						if(sstart.IsEmpty() || (sstop.IsEmpty() && sduration.IsEmpty()))
+							continue;
+
+						int start = TimeToInt(sstart);
+						int stop = !sstop.IsEmpty() ? TimeToInt(sstop) : (start + TimeToInt(sduration));
+
+						ParseSubtitle(pGrandChild, start, stop);
+					}
+				}
+				EndEnumChildren
+			}
+		}
+		EndEnumChildren
+
+		return(true);
+	}
+
+	BeginEnumChildren(pNode, pChild)
+	{
+        if(ParseUSFSubtitles(pChild))
+		{
+			return(true);
+		}
+	}
+	EndEnumChildren
+
+	return(false);
+}
+
+void CUSFSubtitles::ParseMetadata(CComPtr<IXMLDOMNode> pNode, metadata_t& m)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"title")
+	{
+		m.title = GetText(pNode);
+	}
+	else if(name == L"date")
+	{
+		m.date = GetText(pNode);
+	}
+	else if(name == L"comment")
+	{
+		m.comment = GetText(pNode);
+	}
+	else if(name == L"author")
+	{
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"name")
+				m.author.name = GetText(pChild);
+			else if(name == L"email")
+				m.author.email = GetText(pChild);
+			else if(name == L"url")
+				m.author.url = GetText(pChild);
+		}
+		EndEnumChildren
+
+		return;
+	}
+	else if(name == L"language")
+	{
+		m.language.text = GetText(pNode);
+		m.language.code = GetAttrib(L"code", pNode);
+	}
+	else if(name == L"languageext")
+	{
+		m.languageext.text = GetText(pNode);
+		m.languageext.code = GetAttrib(L"code", pNode);
+	}
+
+	BeginEnumChildren(pNode, pChild)
+	{
+		ParseMetadata(pChild, metadata);
+	}
+	EndEnumChildren
+}
+
+void CUSFSubtitles::ParseStyle(CComPtr<IXMLDOMNode> pNode, style_t* s)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"style")
+	{
+		s->name = GetAttrib(L"name", pNode);
+	}
+	else if(name == L"fontstyle")
+	{
+		ParseFontstyle(pNode, s->fontstyle);
+		return;
+	}
+	else if(name == L"position")
+	{
+		ParsePal(pNode, s->pal);
+		return;
+	}
+
+	BeginEnumChildren(pNode, pChild)
+	{
+		ParseStyle(pChild, s);
+	}
+	EndEnumChildren
+}
+
+void CUSFSubtitles::ParseFontstyle(CComPtr<IXMLDOMNode> pNode, fontstyle_t& fs)
+{
+	fs.face = GetAttrib(L"face", pNode);
+	fs.size = GetAttrib(L"size", pNode);
+	fs.color[0] = GetAttrib(L"color", pNode);
+	fs.color[1] = GetAttrib(L"back-color", pNode);
+	fs.color[2] = GetAttrib(L"outline-color", pNode);
+	fs.color[3] = GetAttrib(L"shadow-color", pNode);
+	fs.italic = GetAttrib(L"italic", pNode);
+	fs.weight = GetAttrib(L"weight", pNode);
+	fs.underline = GetAttrib(L"underline", pNode);
+	fs.alpha = GetAttrib(L"alpha", pNode);
+	fs.outline = GetAttrib(L"outline-level", pNode);
+	fs.shadow = GetAttrib(L"shadow-level", pNode);
+	fs.wrap = GetAttrib(L"wrap", pNode);
+}
+
+void CUSFSubtitles::ParsePal(CComPtr<IXMLDOMNode> pNode, posattriblist_t& pal)
+{
+	pal.alignment = GetAttrib(L"alignment", pNode);
+	pal.relativeto = GetAttrib(L"relative-to", pNode);
+	pal.horizontal_margin = GetAttrib(L"horizontal-margin", pNode);
+	pal.vertical_margin = GetAttrib(L"vertical-margin", pNode);
+	pal.rotate[0] = GetAttrib(L"rotate-z", pNode);
+	pal.rotate[1] = GetAttrib(L"rotate-x", pNode);
+	pal.rotate[2] = GetAttrib(L"rotate-y", pNode);
+}
+
+void CUSFSubtitles::ParseEffect(CComPtr<IXMLDOMNode> pNode, effect_t* e)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"effect")
+	{
+		e->name = GetAttrib(L"name", pNode);
+	}
+	else if(name == L"keyframes")
+	{
+		BeginEnumChildren(pNode, pChild)
+		{
+			DeclareNameAndValue(pChild, name, val);
+
+			if(name == L"keyframe")
+			{
+				CAutoPtr<keyframe_t> k(new keyframe_t);
+				if(k)
+				{
+					ParseKeyframe(pChild, k);
+					e->keyframes.AddTail(k);
+				}
+			}
+		}
+		EndEnumChildren
+
+		return;
+	}
+
+	BeginEnumChildren(pNode, pChild)
+	{
+		ParseEffect(pChild, e);
+	}
+	EndEnumChildren
+}
+
+void CUSFSubtitles::ParseKeyframe(CComPtr<IXMLDOMNode> pNode, keyframe_t* k)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"keyframe")
+	{
+		k->position = GetAttrib(L"position", pNode);
+	}
+	else if(name == L"fontstyle")
+	{
+		ParseFontstyle(pNode, k->fontstyle);
+		return;
+	}
+	else if(name == L"position")
+	{
+		ParsePal(pNode, k->pal);
+		return;
+	}
+}
+
+void CUSFSubtitles::ParseSubtitle(CComPtr<IXMLDOMNode> pNode, int start, int stop)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	if(name == L"text" || name == L"karaoke")
+	{
+		CAutoPtr<text_t> t(new text_t);
+		if(t)
+		{
+			t->start = start;
+			t->stop = stop;
+			t->style = GetAttrib(L"style", pNode);
+			t->effect = GetAttrib(L"effect", pNode);
+			ParsePal(pNode, t->pal);
+			ParseText(pNode, t->str);
+			texts.AddTail(t);
+		}
+
+		return;
+	}
+//	else if
+
+	BeginEnumChildren(pNode, pChild)
+	{
+		ParseSubtitle(pChild, start, stop);
+	}
+	EndEnumChildren
+}
+
+void CUSFSubtitles::ParseText(CComPtr<IXMLDOMNode> pNode, CStringW& str)
+{
+	DeclareNameAndValue(pNode, name, val);
+
+	CStringW prefix, postfix;
+
+	if(name == L"b")
+	{
+		prefix = L"{\\b1}";
+		postfix = L"{\\b}";
+	}
+	else if(name == L"i")
+	{
+		prefix = L"{\\i1}";
+		postfix = L"{\\i}";
+	}
+	else if(name == L"u")
+	{
+		prefix = L"{\\u1}";
+		postfix = L"{\\u}";
+	}
+	else if(name == L"font")
+	{
+		fontstyle_t fs;
+		ParseFontstyle(pNode, fs);
+
+		if(!fs.face.IsEmpty()) {prefix += L"{\\fn" + fs.face + L"}"; postfix += L"{\\fn}";}
+		if(!fs.size.IsEmpty()) {prefix += L"{\\fs" + fs.size + L"}"; postfix += L"{\\fs}";}
+		if(!fs.outline.IsEmpty()) {prefix += L"{\\bord" + fs.outline + L"}"; postfix += L"{\\bord}";}
+		if(!fs.shadow.IsEmpty()) {prefix += L"{\\shad" + fs.shadow + L"}"; postfix += L"{\\shad}";}
+
+		for(int i = 0; i < 4; i++)
+		{
+			if(!fs.color[i].IsEmpty())
+			{
+				CStringW s;
+				s.Format(L"{\\%dc&H%06x&}", i+1, ColorToDWORD(fs.color[i]));
+				prefix += s;
+				s.Format(L"{\\%dc}", i+1);
+				postfix += s;
+			}
+		}
+	}
+	else if(name == L"k")
+	{
+		int t = wcstol(GetAttrib(L"t", pNode), NULL, 10);
+		CStringW s;
+		s.Format(L"{\\kf%d}", t / 10);
+		str += s;
+		return;
+	}
+	else if(name == L"br")
+	{
+		str += L"\\N";
+		return;
+	}
+	else if(name == L"#text")
+	{
+		str += GetXML(pNode);
+		return;
+	}
+
+	BeginEnumChildren(pNode, pChild)
+	{
+		CStringW s;
+		ParseText(pChild, s);
+		str += s;
+	}
+	EndEnumChildren
+
+	str = prefix + str + postfix;
+}
+
+void CUSFSubtitles::ParseShape(CComPtr<IXMLDOMNode> pNode)
+{
+	// no specs on this yet
+}
diff --git a/vsfilter/subtitles/USFSubtitles.h b/vsfilter/subtitles/USFSubtitles.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c1a16285fe460c842fd214c8a75d3c351036e6b
--- /dev/null
+++ b/vsfilter/subtitles/USFSubtitles.h
@@ -0,0 +1,68 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include "STS.h"
+
+// metadata
+typedef struct {CStringW name, email, url;} author_t;
+typedef struct {CStringW code, text;} language_t;
+typedef struct {CStringW title, date, comment; author_t author; language_t language, languageext;} metadata_t;
+// style
+typedef struct {CStringW alignment, relativeto, horizontal_margin, vertical_margin, rotate[3];} posattriblist_t;
+typedef struct {CStringW face, size, color[4], weight, italic, underline, alpha, outline, shadow, wrap;} fontstyle_t;
+typedef struct {CStringW name; fontstyle_t fontstyle; posattriblist_t pal;} style_t;
+// effect
+typedef struct {CStringW position; fontstyle_t fontstyle; posattriblist_t pal;} keyframe_t;
+typedef struct {CStringW name; CAutoPtrList<keyframe_t> keyframes;} effect_t;
+// subtitle/text
+typedef struct {int start, stop; CStringW effect, style, str; posattriblist_t pal;} text_t;
+
+class CUSFSubtitles
+{
+	bool ParseUSFSubtitles(CComPtr<IXMLDOMNode> pNode);
+	 void ParseMetadata(CComPtr<IXMLDOMNode> pNode, metadata_t& m);
+	 void ParseStyle(CComPtr<IXMLDOMNode> pNode, style_t* s);
+	  void ParseFontstyle(CComPtr<IXMLDOMNode> pNode, fontstyle_t& fs);
+	  void ParsePal(CComPtr<IXMLDOMNode> pNode, posattriblist_t& pal);
+	 void ParseEffect(CComPtr<IXMLDOMNode> pNode, effect_t* e);
+	  void ParseKeyframe(CComPtr<IXMLDOMNode> pNode, keyframe_t* k);
+	 void ParseSubtitle(CComPtr<IXMLDOMNode> pNode, int start, int stop);
+	  void ParseText(CComPtr<IXMLDOMNode> pNode, CStringW& assstr);
+	  void ParseShape(CComPtr<IXMLDOMNode> pNode);
+
+public:
+	CUSFSubtitles();
+	virtual ~CUSFSubtitles();
+
+	bool Read(LPCTSTR fn);
+//	bool Write(LPCTSTR fn); // TODO
+
+	metadata_t metadata;
+	CAutoPtrList<style_t> styles;
+	CAutoPtrList<effect_t> effects;
+	CAutoPtrList<text_t> texts;
+
+	bool ConvertToSTS(CSimpleTextSubtitle& sts);
+//	bool ConvertFromSTS(CSimpleTextSubtitle& sts); // TODO
+};
diff --git a/vsfilter/subtitles/VobSubFile.cpp b/vsfilter/subtitles/VobSubFile.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d91e4827050bea88233ffcdbd6342b47995e3b1d
--- /dev/null
+++ b/vsfilter/subtitles/VobSubFile.cpp
@@ -0,0 +1,2368 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include <winioctl.h>
+#include "TextFile.h"
+#include "..\include\unrar\unrar.h"
+#include "VobSubFile.h"
+
+//
+
+struct lang_type {unsigned short id; LPCSTR lang_long;} lang_tbl[] =
+{
+	{'--', "(Not detected)"},
+	{'cc', "Closed Caption"},
+	{'aa', "Afar"},
+	{'ab', "Abkhazian"},
+	{'af', "Afrikaans"},
+	{'am', "Amharic"},
+	{'ar', "Arabic"},
+	{'as', "Assamese"},
+	{'ay', "Aymara"},
+	{'az', "Azerbaijani"},
+	{'ba', "Bashkir"},
+	{'be', "Byelorussian"},
+	{'bg', "Bulgarian"},
+	{'bh', "Bihari"},
+	{'bi', "Bislama"},
+	{'bn', "Bengali; Bangla"},
+	{'bo', "Tibetan"},
+	{'br', "Breton"},
+	{'ca', "Catalan"},
+	{'co', "Corsican"},
+	{'cs', "Czech"},
+	{'cy', "Welsh"},
+	{'da', "Dansk"},
+	{'de', "Deutsch"},
+	{'dz', "Bhutani"},
+	{'el', "Greek"},
+	{'en', "English"},
+	{'eo', "Esperanto"},
+	{'es', "Espanol"},
+	{'et', "Estonian"},
+	{'eu', "Basque"},
+	{'fa', "Persian"},
+	{'fi', "Finnish"},
+	{'fj', "Fiji"},
+	{'fo', "Faroese"},
+	{'fr', "Francais"},
+	{'fy', "Frisian"},
+	{'ga', "Irish"},
+	{'gd', "Scots Gaelic"},
+	{'gl', "Galician"},
+	{'gn', "Guarani"},
+	{'gu', "Gujarati"},
+	{'ha', "Hausa"},
+	{'he', "Hebrew"},
+	{'hi', "Hindi"},
+	{'hr', "Hrvatski"},
+	{'hu', "Hungarian"},
+	{'hy', "Armenian"},
+	{'ia', "Interlingua"},
+	{'id', "Indonesian"},
+	{'ie', "Interlingue"},
+	{'ik', "Inupiak"},
+	{'in', "Indonesian"},
+	{'is', "Islenska"},
+	{'it', "Italiano"},
+	{'iu', "Inuktitut"},
+	{'iw', "Hebrew"},
+	{'ja', "Japanese"},
+	{'ji', "Yiddish"},
+	{'jw', "Javanese"},
+	{'ka', "Georgian"},
+	{'kk', "Kazakh"},
+	{'kl', "Greenlandic"},
+	{'km', "Cambodian"},
+	{'kn', "Kannada"},
+	{'ko', "Korean"},
+	{'ks', "Kashmiri"},
+	{'ku', "Kurdish"},
+	{'ky', "Kirghiz"},
+	{'la', "Latin"},
+	{'ln', "Lingala"},
+	{'lo', "Laothian"},
+	{'lt', "Lithuanian"},
+	{'lv', "Latvian, Lettish"},
+	{'mg', "Malagasy"},
+	{'mi', "Maori"},
+	{'mk', "Macedonian"},
+	{'ml', "Malayalam"},
+	{'mn', "Mongolian"},
+	{'mo', "Moldavian"},
+	{'mr', "Marathi"},
+	{'ms', "Malay"},
+	{'mt', "Maltese"},
+	{'my', "Burmese"},
+	{'na', "Nauru"},
+	{'ne', "Nepali"},
+	{'nl', "Nederlands"},
+	{'no', "Norsk"},
+	{'oc', "Occitan"},
+	{'om', "(Afan) Oromo"},
+	{'or', "Oriya"},
+	{'pa', "Punjabi"},
+	{'pl', "Polish"},
+	{'ps', "Pashto, Pushto"},
+	{'pt', "Portugues"},
+	{'qu', "Quechua"},
+	{'rm', "Rhaeto-Romance"},
+	{'rn', "Kirundi"},
+	{'ro', "Romanian"},
+	{'ru', "Russian"},
+	{'rw', "Kinyarwanda"},
+	{'sa', "Sanskrit"},
+	{'sd', "Sindhi"},
+	{'sg', "Sangho"},
+	{'sh', "Serbo-Croatian"},
+	{'si', "Sinhalese"},
+	{'sk', "Slovak"},
+	{'sl', "Slovenian"},
+	{'sm', "Samoan"},
+	{'sn', "Shona"},
+	{'so', "Somali"},
+	{'sq', "Albanian"},
+	{'sr', "Serbian"},
+	{'ss', "Siswati"},
+	{'st', "Sesotho"},
+	{'su', "Sundanese"},
+	{'sv', "Svenska"},
+	{'sw', "Swahili"},
+	{'ta', "Tamil"},
+	{'te', "Telugu"},
+	{'tg', "Tajik"},
+	{'th', "Thai"},
+	{'ti', "Tigrinya"},
+	{'tk', "Turkmen"},
+	{'tl', "Tagalog"},
+	{'tn', "Setswana"},
+	{'to', "Tonga"},
+	{'tr', "Turkish"},
+	{'ts', "Tsonga"},
+	{'tt', "Tatar"},
+	{'tw', "Twi"},
+	{'ug', "Uighur"},
+	{'uk', "Ukrainian"},
+	{'ur', "Urdu"},
+	{'uz', "Uzbek"},
+	{'vi', "Vietnamese"},
+	{'vo', "Volapuk"},
+	{'wo', "Wolof"},
+	{'xh', "Xhosa"},
+	{'yi', "Yiddish"},				// formerly ji
+	{'yo', "Yoruba"},
+	{'za', "Zhuang"},
+	{'zh', "Chinese"},
+	{'zu', "Zulu"},
+};
+
+int find_lang(unsigned short id)
+{
+	int mid, lo = 0, hi = countof(lang_tbl) - 1;
+
+	while(lo < hi)
+	{
+		mid = (lo + hi) >> 1;
+		if(id < lang_tbl[mid].id) hi = mid;
+		else if(id > lang_tbl[mid].id) lo = mid + 1;
+		else return(mid);
+	}
+
+	return(id == lang_tbl[lo].id ? lo : 0);
+}
+
+CString FindLangFromId(WORD id)
+{
+	return CString(lang_tbl[find_lang(id)].lang_long);
+}
+
+//
+// CVobSubFile
+//
+
+CVobSubFile::CVobSubFile(CCritSec* pLock)
+	: ISubPicProviderImpl(pLock)
+	, m_sub(1024*1024)
+{
+}
+
+CVobSubFile::~CVobSubFile()
+{
+}
+
+//
+
+bool CVobSubFile::Copy(CVobSubFile& vsf)
+{
+	Close();
+
+	*(CVobSubSettings*)this = *(CVobSubSettings*)&vsf;
+	m_title = vsf.m_title;
+	m_iLang = vsf.m_iLang;
+
+	m_sub.SetLength(vsf.m_sub.GetLength());
+	m_sub.SeekToBegin();
+
+	for(int i = 0; i < 32; i++)
+	{
+		SubLang& src = vsf.m_langs[i];
+		SubLang& dst = m_langs[i];
+
+		dst.id = src.id;
+		dst.name = src.name;
+		dst.alt = src.alt;
+
+		for(int j = 0; j < src.subpos.GetCount(); j++)
+		{
+			SubPos& sp = src.subpos[j];
+			if(!sp.fValid) continue;
+
+			if(sp.filepos != vsf.m_sub.Seek(sp.filepos, CFile::begin))
+				continue;
+
+			sp.filepos = m_sub.GetPosition();
+
+			BYTE buff[2048];
+			vsf.m_sub.Read(buff, 2048);
+			m_sub.Write(buff, 2048);
+
+			WORD packetsize = (buff[buff[0x16]+0x18]<<8) | buff[buff[0x16]+0x19];
+
+			for(int k = 0, size, sizeleft = packetsize - 4; 
+				k < packetsize - 4; 
+				k += size, sizeleft -= size)
+			{
+				int hsize = buff[0x16]+0x18 + ((buff[0x15]&0x80) ? 4 : 0);
+				size = min(sizeleft, 2048 - hsize);
+	
+				if(size != sizeleft) 
+				{
+					while(vsf.m_sub.Read(buff, 2048))
+					{
+						if(!(buff[0x15]&0x80) && buff[buff[0x16]+0x17] == (i|0x20)) break;
+					}
+
+					m_sub.Write(buff, 2048);
+				}
+			}
+
+			dst.subpos.Add(sp);
+		}
+	}
+
+	m_sub.SetLength(m_sub.GetPosition());
+
+	return(true);
+}
+
+//
+
+void CVobSubFile::TrimExtension(CString& fn)
+{
+	int i = fn.ReverseFind('.');
+	if(i > 0)
+	{
+		CString ext = fn.Mid(i).MakeLower();
+		if(ext == _T(".ifo") || ext == _T(".idx") || ext == _T(".sub")
+		|| ext == _T(".sst") || ext == _T(".son") || ext == _T(".rar"))
+			fn = fn.Left(i);
+	}
+}
+
+bool CVobSubFile::Open(CString fn)
+{
+	TrimExtension(fn);
+
+	do
+	{
+		Close();
+
+		int ver;
+		if(!ReadIdx(fn + _T(".idx"), ver))
+			break;
+
+		if(ver < 6 && !ReadIfo(fn + _T(".ifo")))
+			break;
+
+		if(!ReadSub(fn + _T(".sub")) && !ReadRar(fn + _T(".rar")))
+			break;
+
+		m_title = fn;
+
+		for(int i = 0; i < 32; i++)
+		{
+			CAtlArray<SubPos>& sp = m_langs[i].subpos;
+
+			for(int j = 0; j < sp.GetCount(); j++)
+			{
+				sp[j].stop = sp[j].start;
+				sp[j].fForced = false;
+
+				int packetsize = 0, datasize = 0;
+				BYTE* buff = GetPacket(j, packetsize, datasize, i);
+				if(!buff) continue;
+
+				m_img.delay = j < (sp.GetCount()-1) ? sp[j+1].start - sp[j].start : 3000;
+				m_img.GetPacketInfo(buff, packetsize, datasize);
+				if(j < (sp.GetCount()-1)) m_img.delay = min(m_img.delay, sp[j+1].start - sp[j].start);
+
+				sp[j].stop = sp[j].start + m_img.delay;
+				sp[j].fForced = m_img.fForced;
+
+				if(j > 0 && sp[j-1].stop > sp[j].start)
+					sp[j-1].stop = sp[j].start;
+
+				delete [] buff;
+			}
+		}
+
+		return(true);
+	}
+	while(false);
+
+	Close();
+
+	return(false);
+}
+
+bool CVobSubFile::Save(CString fn, SubFormat sf)
+{
+	TrimExtension(fn);
+
+	CVobSubFile vsf(NULL);
+	if(!vsf.Copy(*this))
+		return(false);
+
+	switch(sf)
+	{
+	case VobSub: return vsf.SaveVobSub(fn); break;
+	case WinSubMux: return vsf.SaveWinSubMux(fn); break;
+	case Scenarist: return vsf.SaveScenarist(fn); break;
+	case Maestro: return vsf.SaveMaestro(fn); break;
+	default: break;
+	}
+
+	return(false);
+}
+
+void CVobSubFile::Close()
+{
+	InitSettings();
+	m_title.Empty();
+	m_sub.SetLength(0);
+	m_img.Invalidate();
+	m_iLang = -1;
+	for(int i = 0; i < 32; i++)
+	{
+		m_langs[i].id = 0;
+		m_langs[i].name.Empty();
+		m_langs[i].alt.Empty();
+		m_langs[i].subpos.RemoveAll();
+	}
+}
+
+//
+
+bool CVobSubFile::ReadIdx(CString fn, int& ver)
+{
+	CWebTextFile f;
+	if(!f.Open(fn))
+		return(false);
+
+	bool fError = false;
+
+	int id = -1, delay = 0, vobid = -1, cellid = -1;
+	__int64 celltimestamp = 0;
+
+	CString str;
+	for(int line = 0; !fError && f.ReadString(str); line++)
+	{
+		str.Trim();
+
+		if(line == 0)
+		{
+			TCHAR buff[] = _T("VobSub index file, v");
+
+			const TCHAR* s = str;
+
+			int i = str.Find(buff);
+			if(i < 0 || _stscanf(&s[i+_tcslen(buff)], _T("%d"), &ver) != 1
+			|| ver > VOBSUBIDXVER)
+			{
+				AfxMessageBox(_T("Wrong file version!"));
+				fError = true;
+				continue;
+			}
+		}
+		else if(!str.GetLength())
+		{
+			continue;
+		}
+		else if(str[0] == _T('#'))
+		{
+			TCHAR buff[] = _T("Vob/Cell ID:");
+
+            const TCHAR* s = str;
+
+			int i = str.Find(buff);
+			if(i >= 0)
+			{
+				_stscanf(&s[i+_tcslen(buff)], _T("%d, %d (PTS: %d)"), &vobid, &cellid, &celltimestamp);
+			}
+
+			continue;
+		}
+
+		int i = str.Find(':');
+		if(i <= 0) continue;
+
+		CString entry = str.Left(i).MakeLower();
+
+		str = str.Mid(i+1);
+		str.Trim();
+		if(str.IsEmpty()) continue;
+
+		if(entry == _T("size"))
+		{
+			int x, y;
+			if(_stscanf(str, _T("%dx%d"), &x, &y) != 2) fError = true;
+			m_size.cx = x;
+			m_size.cy = y;
+		}
+		else if(entry == _T("org"))
+		{
+			if(_stscanf(str, _T("%d,%d"), &m_x, &m_y) != 2) fError = true;
+			else m_org = CPoint(m_x, m_y);
+		}
+		else if(entry == _T("scale"))
+		{
+			if(ver < 5) 
+			{
+				int scale = 100;
+				if(_stscanf(str, _T("%d%%"), &scale) != 1) fError = true;
+				m_scale_x = m_scale_y = scale;
+			}
+			else 
+			{
+				if(_stscanf(str, _T("%d%%,%d%%"), &m_scale_x, &m_scale_y) != 2) fError = true;
+			}
+		}
+		else if(entry == _T("alpha"))
+		{
+			if(_stscanf(str, _T("%d"), &m_alpha) != 1) fError = true;
+		}
+		else if(entry == _T("smooth"))
+		{
+			str.MakeLower();
+
+			if(str.Find(_T("old")) >= 0 || str.Find(_T("2")) >= 0) m_fSmooth = 2;
+			else if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fSmooth = 1;
+			else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fSmooth = 0;
+			else fError = true;
+		}
+		else if(entry == _T("fadein/out"))
+		{
+			if(_stscanf(str, _T("%d,%d"), &m_fadein, &m_fadeout) != 2) fError = true;
+		}
+		else if(entry == _T("align"))
+		{
+			str.MakeLower();
+
+			int i = 0, j = 0;
+			for(CString token = str.Tokenize(_T(" "), i); 
+				j < 3 && !fError && !token.IsEmpty(); 
+				token = str.Tokenize(_T(" "), i), j++)
+			{
+				if(j == 0)
+				{
+					if(token == _T("on") || token == _T("1")) m_fAlign = true;
+					else if(token == _T("off") || token == _T("0")) m_fAlign = false;
+					else fError = true;
+				}
+				else if(j == 1)
+				{
+					if(token == _T("at")) {j--; continue;}
+
+					if(token == _T("left")) m_alignhor = 0;
+					else if(token == _T("center")) m_alignhor = 1;
+					else if(token == _T("right")) m_alignhor = 2;
+					else fError = true;
+				}
+				else if(j == 2)
+				{
+					if(token == _T("top")) m_alignver = 0;
+					else if(token == _T("center")) m_alignver = 1;
+					else if(token == _T("bottom")) m_alignver = 2;
+					else fError = true;
+				}
+			}
+		}
+		else if(entry == _T("time offset"))
+		{
+			bool fNegative = false;
+			if(str[0] == '-') fNegative = true;
+			str.TrimLeft(_T("+-"));
+
+			TCHAR c;
+			int hh, mm, ss, ms;
+			int n = _stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms);
+			
+			m_toff = n == 1 
+					? hh * (fNegative ? -1 : 1)
+					: n == 4+3
+					? (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1)
+					: fError = true, 0;
+		}
+		else if(entry == _T("forced subs"))
+		{
+			str.MakeLower();
+
+			if(str.Find(_T("on")) >= 0 || str.Find(_T("1")) >= 0) m_fOnlyShowForcedSubs = true;
+			else if(str.Find(_T("off")) >= 0 || str.Find(_T("0")) >= 0) m_fOnlyShowForcedSubs = false;
+			else fError = true;
+		}
+		else if(entry == _T("langidx"))
+		{
+			if(_stscanf(str, _T("%d"), &m_iLang) != 1) fError = true;
+		}
+		else if(entry == _T("palette"))
+		{
+			if(_stscanf(str, _T("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x"), 
+				&m_orgpal[0], &m_orgpal[1], &m_orgpal[2], &m_orgpal[3],
+				&m_orgpal[4], &m_orgpal[5], &m_orgpal[6], &m_orgpal[7],
+				&m_orgpal[8], &m_orgpal[9], &m_orgpal[10], &m_orgpal[11],
+				&m_orgpal[12], &m_orgpal[13], &m_orgpal[14], &m_orgpal[15]
+				) != 16) fError = true;
+		}
+		else if(entry == _T("custom colors"))
+		{
+			str.MakeLower();
+
+			if(str.Find(_T("on")) == 0 || str.Find(_T("1")) == 0) m_fCustomPal = true;
+			else if(str.Find(_T("off")) == 0 || str.Find(_T("0")) == 0) m_fCustomPal = false;
+			else fError = true;
+
+			i = str.Find(_T("tridx:"));
+			if(i < 0) {fError = true; continue;}
+			str = str.Mid(i + (int)_tcslen(_T("tridx:")));
+
+			int tridx;
+			if(_stscanf(str, _T("%x"), &tridx) != 1) {fError = true; continue;}
+			tridx = ((tridx&0x1000)>>12) | ((tridx&0x100)>>7) | ((tridx&0x10)>>2) | ((tridx&1)<<3);
+
+			i = str.Find(_T("colors:"));
+			if(i < 0) {fError = true; continue;}
+			str = str.Mid(i + (int)_tcslen(_T("colors:")));
+
+			RGBQUAD pal[4];
+			if(_stscanf(str, _T("%x,%x,%x,%x"), &pal[0], &pal[1], &pal[2], &pal[3]) != 4) {fError = true; continue;}
+
+			SetCustomPal(pal, tridx);
+		}
+		else if(entry == _T("id"))
+		{
+			str.MakeLower();
+
+			int langid = ((str[0]&0xff)<<8)|(str[1]&0xff);
+
+			i = str.Find(_T("index:"));
+			if(i < 0) {fError = true; continue;}
+			str = str.Mid(i + (int)_tcslen(_T("index:")));
+
+			if(_stscanf(str, _T("%d"), &id) != 1 || id < 0 || id >= 32) {fError = true; continue;}
+
+			m_langs[id].id = langid;
+			m_langs[id].name = lang_tbl[find_lang(langid)].lang_long;
+			m_langs[id].alt = lang_tbl[find_lang(langid)].lang_long;
+
+			delay = 0;
+		}
+		else if(id >= 0 && entry == _T("alt"))
+		{
+			m_langs[id].alt = str;
+		}
+		else if(id >= 0 && entry == _T("delay"))
+		{
+			bool fNegative = false;
+			if(str[0] == '-') fNegative = true;
+			str.TrimLeft(_T("+-"));
+
+			TCHAR c;
+			int hh, mm, ss, ms;
+			if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3)  {fError = true; continue;}
+
+			delay += (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1);
+		}
+		else if(id >= 0 && entry == _T("timestamp"))
+		{
+			SubPos sb;
+
+			sb.vobid = vobid;
+			sb.cellid = cellid;
+			sb.celltimestamp = celltimestamp;
+			sb.fValid = true;
+
+			bool fNegative = false;
+			if(str[0] == '-') fNegative = true;
+			str.TrimLeft(_T("+-"));
+
+			TCHAR c;
+			int hh, mm, ss, ms;
+			if(_stscanf(str, _T("%d%c%d%c%d%c%d"), &hh, &c, &mm, &c, &ss, &c, &ms) != 4+3)  {fError = true; continue;}
+
+			sb.start = (hh*60*60*1000 + mm*60*1000 + ss*1000 + ms) * (fNegative ? -1 : 1) + delay;
+
+			i = str.Find(_T("filepos:"));
+			if(i < 0) {fError = true; continue;}
+			str = str.Mid(i + (int)_tcslen(_T("filepos:")));
+
+			if(_stscanf(str, _T("%I64x"), &sb.filepos) != 1) {fError = true; continue;}
+
+			if(delay < 0 && m_langs[id].subpos.GetCount() > 0)
+			{
+				__int64 ts = m_langs[id].subpos[m_langs[id].subpos.GetCount()-1].start;
+					
+				if(sb.start < ts)
+				{
+					delay += (int)(ts - sb.start);
+					sb.start = ts;
+				}
+			}
+
+			m_langs[id].subpos.Add(sb);
+		}
+		else fError = true;
+	}
+
+	return(!fError);
+}
+
+bool CVobSubFile::ReadSub(CString fn)
+{
+	CFile f;
+	if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+
+	m_sub.SetLength(f.GetLength());
+	m_sub.SeekToBegin();
+
+	int len;
+	BYTE buff[2048];
+	while((len = f.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
+		m_sub.Write(buff, len);
+
+	return(true);
+}
+
+static unsigned char* RARbuff = NULL;
+static unsigned int RARpos = 0;
+
+static int PASCAL MyProcessDataProc(unsigned char* Addr, int Size)
+{
+	ASSERT(RARbuff);
+
+	memcpy(&RARbuff[RARpos], Addr, Size);
+	RARpos += Size;
+
+	return(1);
+}
+
+bool CVobSubFile::ReadRar(CString fn)
+{
+	HMODULE h = LoadLibrary(_T("unrar.dll"));
+	if(!h) return(false);
+
+	RAROpenArchiveEx OpenArchiveEx = (RAROpenArchiveEx)GetProcAddress(h, "RAROpenArchiveEx");
+	RARCloseArchive CloseArchive = (RARCloseArchive)GetProcAddress(h, "RARCloseArchive");
+	RARReadHeaderEx ReadHeaderEx = (RARReadHeaderEx)GetProcAddress(h, "RARReadHeaderEx");
+	RARProcessFile ProcessFile = (RARProcessFile)GetProcAddress(h, "RARProcessFile");
+	RARSetChangeVolProc SetChangeVolProc = (RARSetChangeVolProc)GetProcAddress(h, "RARSetChangeVolProc");
+	RARSetProcessDataProc SetProcessDataProc = (RARSetProcessDataProc)GetProcAddress(h, "RARSetProcessDataProc");
+	RARSetPassword SetPassword = (RARSetPassword)GetProcAddress(h, "RARSetPassword");
+
+	if(!(OpenArchiveEx && CloseArchive && ReadHeaderEx && ProcessFile 
+	&& SetChangeVolProc && SetProcessDataProc && SetPassword))
+	{
+		FreeLibrary(h);
+		return(false);
+	}
+
+	struct RAROpenArchiveDataEx ArchiveDataEx;
+	memset(&ArchiveDataEx, 0, sizeof(ArchiveDataEx));
+#ifdef UNICODE
+	ArchiveDataEx.ArcNameW = (LPTSTR)(LPCTSTR)fn;
+	char fnA[MAX_PATH];
+	if(wcstombs(fnA, fn, fn.GetLength()+1) == -1) fnA[0] = 0;
+	ArchiveDataEx.ArcName = fnA;
+#else
+	ArchiveDataEx.ArcName = (LPTSTR)(LPCTSTR)fn;
+#endif
+	ArchiveDataEx.OpenMode = RAR_OM_EXTRACT;
+	ArchiveDataEx.CmtBuf = 0;
+	HANDLE hrar = OpenArchiveEx(&ArchiveDataEx);
+	if(!hrar) 
+	{
+		FreeLibrary(h);
+		return(false);
+	}
+
+	SetProcessDataProc(hrar, MyProcessDataProc);
+
+	struct RARHeaderDataEx HeaderDataEx;
+	HeaderDataEx.CmtBuf = NULL;
+	
+	while(ReadHeaderEx(hrar, &HeaderDataEx) == 0)
+	{
+#ifdef UNICODE
+		CString subfn(HeaderDataEx.FileNameW);
+#else
+		CString subfn(HeaderDataEx.FileName);
+#endif
+
+		if(!subfn.Right(4).CompareNoCase(_T(".sub")))
+		{
+			CAutoVectorPtr<BYTE> buff;
+			if(!buff.Allocate(HeaderDataEx.UnpSize))
+			{
+				CloseArchive(hrar);
+				FreeLibrary(h);
+				return(false);
+			}
+
+			RARbuff = buff;
+			RARpos = 0;
+
+			if(ProcessFile(hrar, RAR_TEST, NULL, NULL))
+			{
+				CloseArchive(hrar);
+				FreeLibrary(h);
+				
+				return(false);
+			}
+
+			m_sub.SetLength(HeaderDataEx.UnpSize);
+			m_sub.SeekToBegin();
+			m_sub.Write(buff, HeaderDataEx.UnpSize);
+			m_sub.SeekToBegin();
+
+			RARbuff = NULL;
+			RARpos = 0;
+
+			break;
+		}
+
+		ProcessFile(hrar, RAR_SKIP, NULL, NULL);
+	}
+
+	CloseArchive(hrar);
+	FreeLibrary(h);
+
+	return(true);
+}
+
+#define ReadBEdw(var) \
+    f.Read(&((BYTE*)&var)[3], 1); \
+	f.Read(&((BYTE*)&var)[2], 1); \
+	f.Read(&((BYTE*)&var)[1], 1); \
+	f.Read(&((BYTE*)&var)[0], 1); \
+
+bool CVobSubFile::ReadIfo(CString fn)
+{
+	CFile f;
+	if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+
+	/* PGC1 */
+
+	f.Seek(0xc0+0x0c, SEEK_SET);
+
+	DWORD pos;
+	ReadBEdw(pos);
+
+	f.Seek(pos*0x800 + 0x0c, CFile::begin);
+
+	DWORD offset;
+	ReadBEdw(offset);
+	
+	/* Subpic palette */
+
+	f.Seek(pos*0x800 + offset + 0xa4, CFile::begin);
+
+	for(int i = 0; i < 16; i++) 
+	{
+		BYTE y, u, v, tmp;
+
+		f.Read(&tmp, 1);
+		f.Read(&y, 1);
+		f.Read(&u, 1);
+		f.Read(&v, 1);
+
+		y = (y-16)*255/219;
+
+		m_orgpal[i].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
+		m_orgpal[i].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
+		m_orgpal[i].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
+	}
+
+	return(true);
+}
+
+bool CVobSubFile::WriteIdx(CString fn)
+{
+	CTextFile f;
+	if(!f.Save(fn, CTextFile::ASCII))
+		return(false);
+
+	CString str;
+	str.Format(_T("# VobSub index file, v%d (do not modify this line!)\n"), VOBSUBIDXVER);
+
+	f.WriteString(str);
+	f.WriteString(_T("# \n"));
+	f.WriteString(_T("# To repair desyncronization, you can insert gaps this way:\n"));
+	f.WriteString(_T("# (it usually happens after vob id changes)\n"));
+	f.WriteString(_T("# \n"));
+	f.WriteString(_T("#\t delay: [sign]hh:mm:ss:ms\n"));
+	f.WriteString(_T("# \n"));
+	f.WriteString(_T("# Where:\n"));
+	f.WriteString(_T("#\t [sign]: +, - (optional)\n"));
+	f.WriteString(_T("#\t hh: hours (0 <= hh)\n"));
+	f.WriteString(_T("#\t mm/ss: minutes/seconds (0 <= mm/ss <= 59)\n"));
+	f.WriteString(_T("#\t ms: milliseconds (0 <= ms <= 999)\n"));
+	f.WriteString(_T("# \n"));
+	f.WriteString(_T("#\t Note: You can't position a sub before the previous with a negative value.\n"));
+	f.WriteString(_T("# \n"));	
+	f.WriteString(_T("# You can also modify timestamps or delete a few subs you don't like.\n"));
+	f.WriteString(_T("# Just make sure they stay in increasing order.\n"));
+	f.WriteString(_T("\n"));
+	f.WriteString(_T("\n"));
+
+	// Settings
+
+	f.WriteString(_T("# Settings\n\n"));
+
+	f.WriteString(_T("# Original frame size\n"));
+	str.Format(_T("size: %dx%d\n\n"), m_size.cx, m_size.cy);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Origin, relative to the upper-left corner, can be overloaded by aligment\n"));
+	str.Format(_T("org: %d, %d\n\n"), m_x, m_y);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Image scaling (hor,ver), origin is at the upper-left corner or at the alignment coord (x, y)\n"));
+	str.Format(_T("scale: %d%%, %d%%\n\n"), m_scale_x, m_scale_y);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Alpha blending\n"));
+	str.Format(_T("alpha: %d%%\n\n"), m_alpha);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Smoothing for very blocky images (use OLD for no filtering)\n"));
+	str.Format(_T("smooth: %s\n\n"), m_fSmooth == 0 ? _T("OFF") : m_fSmooth == 1 ? _T("ON") : _T("OLD"));
+	f.WriteString(str);
+
+	f.WriteString(_T("# In millisecs\n"));
+	str.Format(_T("fadein/out: %d, %d\n\n"), m_fadein, m_fadeout);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Force subtitle placement relative to (org.x, org.y)\n"));
+	str.Format(_T("align: %s %s %s\n\n"), 
+		m_fAlign ? _T("ON at") : _T("OFF at"), 
+		m_alignhor == 0 ? _T("LEFT") : m_alignhor == 1 ? _T("CENTER") : m_alignhor == 2 ? _T("RIGHT") : _T(""), 
+		m_alignver == 0 ? _T("TOP") : m_alignver == 1 ? _T("CENTER") : m_alignver == 2 ? _T("BOTTOM") : _T(""));
+	f.WriteString(str);
+
+	f.WriteString(_T("# For correcting non-progressive desync. (in millisecs or hh:mm:ss:ms)\n"));
+	f.WriteString(_T("# Note: Not effective in DirectVobSub, use \"delay: ... \" instead.\n"));
+	str.Format(_T("time offset: %d\n\n"), m_toff);
+	f.WriteString(str);
+
+	f.WriteString(_T("# ON: displays only forced subtitles, OFF: shows everything\n"));
+	str.Format(_T("forced subs: %s\n\n"), m_fOnlyShowForcedSubs ? _T("ON") : _T("OFF"));
+	f.WriteString(str);
+
+	f.WriteString(_T("# The original palette of the DVD\n"));
+	str.Format(_T("palette: %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x, %06x\n\n"), 
+		*((unsigned int*)&m_orgpal[0])&0xffffff,
+		*((unsigned int*)&m_orgpal[1])&0xffffff,
+		*((unsigned int*)&m_orgpal[2])&0xffffff,
+		*((unsigned int*)&m_orgpal[3])&0xffffff,
+		*((unsigned int*)&m_orgpal[4])&0xffffff,
+		*((unsigned int*)&m_orgpal[5])&0xffffff,
+		*((unsigned int*)&m_orgpal[6])&0xffffff,
+		*((unsigned int*)&m_orgpal[7])&0xffffff,
+		*((unsigned int*)&m_orgpal[8])&0xffffff,
+		*((unsigned int*)&m_orgpal[9])&0xffffff,
+		*((unsigned int*)&m_orgpal[10])&0xffffff,
+		*((unsigned int*)&m_orgpal[11])&0xffffff,
+		*((unsigned int*)&m_orgpal[12])&0xffffff,
+		*((unsigned int*)&m_orgpal[13])&0xffffff,
+		*((unsigned int*)&m_orgpal[14])&0xffffff,
+		*((unsigned int*)&m_orgpal[15])&0xffffff);
+	f.WriteString(str);
+
+	int tridx = (!!(m_tridx&1))*0x1000 + (!!(m_tridx&2))*0x100 + (!!(m_tridx&4))*0x10 + (!!(m_tridx&8));
+
+	f.WriteString(_T("# Custom colors (transp idxs and the four colors)\n"));
+	str.Format(_T("custom colors: %s, tridx: %04x, colors: %06x, %06x, %06x, %06x\n\n"), 
+		m_fCustomPal ? _T("ON") : _T("OFF"),
+		tridx, 
+		*((unsigned int*)&m_cuspal[0])&0xffffff,
+		*((unsigned int*)&m_cuspal[1])&0xffffff,
+		*((unsigned int*)&m_cuspal[2])&0xffffff,
+		*((unsigned int*)&m_cuspal[3])&0xffffff);
+	f.WriteString(str);
+
+	f.WriteString(_T("# Language index in use\n"));
+	str.Format(_T("langidx: %d\n\n"), m_iLang);
+	f.WriteString(str);
+
+	// Subs
+
+	for(int i = 0; i < 32; i++)
+	{
+		SubLang& sl = m_langs[i];
+
+		CAtlArray<SubPos>& sp = sl.subpos;
+		if(sp.IsEmpty() && !sl.id) continue;
+
+		str.Format(_T("# %s\n"), sl.name);
+		f.WriteString(str);
+
+		ASSERT(sl.id);
+		if(!sl.id) sl.id = '--';
+		str.Format(_T("id: %c%c, index: %d\n"), sl.id>>8, sl.id&0xff, i);
+		f.WriteString(str);
+
+		str.Format(_T("# Decomment next line to activate alternative name in DirectVobSub / Windows Media Player 6.x\n"));
+		f.WriteString(str);
+		str.Format(_T("alt: %s\n"), sl.alt);
+		if(sl.name == sl.alt) str = _T("# ") + str;
+		f.WriteString(str);
+
+		char vobid = -1, cellid = -1;
+
+		for(int j = 0; j < sp.GetCount(); j++) 
+		{
+			if(!sp[j].fValid) continue;
+
+			if(sp[j].vobid != vobid || sp[j].cellid != cellid)
+			{
+				str.Format(_T("# Vob/Cell ID: %d, %d (PTS: %d)\n"), sp[j].vobid, sp[j].cellid, sp[j].celltimestamp);
+				f.WriteString(str);
+				vobid = sp[j].vobid;
+				cellid = sp[j].cellid;
+			}
+			
+			str.Format(_T("timestamp: %s%02d:%02d:%02d:%03d, filepos: %09I64x\n"), 
+				sp[j].start < 0 ? _T("-") : _T(""),
+				abs(int((sp[j].start/1000/60/60)%60)), 
+				abs(int((sp[j].start/1000/60)%60)), 
+				abs(int((sp[j].start/1000)%60)), 
+				abs(int((sp[j].start)%1000)), 
+				sp[j].filepos);
+			f.WriteString(str);
+		}
+
+		f.WriteString(_T("\n"));
+	}
+
+	return(true);
+}
+
+bool CVobSubFile::WriteSub(CString fn)
+{
+	CFile f;
+	if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+
+	if(m_sub.GetLength() == 0)
+		return(true); // nothing to do...
+
+	m_sub.SeekToBegin();
+
+	int len;
+	BYTE buff[2048];
+	while((len = m_sub.Read(buff, sizeof(buff))) > 0 && *(DWORD*)buff == 0xba010000)
+		f.Write(buff, len);
+
+	return(true);
+}
+
+//
+
+BYTE* CVobSubFile::GetPacket(int idx, int& packetsize, int& datasize, int iLang)
+{
+	BYTE* ret = NULL;
+
+	if(iLang < 0 || iLang >= 32) iLang = m_iLang;
+	CAtlArray<SubPos>& sp = m_langs[iLang].subpos;
+
+	do
+	{
+		if(idx < 0 || idx >= sp.GetCount())
+			break;
+
+		if(m_sub.Seek(sp[idx].filepos, CFile::begin) != sp[idx].filepos) 
+			break;
+
+		BYTE buff[0x800];
+		if(sizeof(buff) != m_sub.Read(buff, sizeof(buff)))
+			break;
+
+		BYTE offset = buff[0x16];
+
+		// let's check a few things to make sure...
+		if(*(DWORD*)&buff[0x00] != 0xba010000
+		|| *(DWORD*)&buff[0x0e] != 0xbd010000
+		|| !(buff[0x15] & 0x80)	
+		|| (buff[0x17] & 0xf0) != 0x20
+		|| (buff[buff[0x16] + 0x17] & 0xe0) != 0x20
+		|| (buff[buff[0x16] + 0x17] & 0x1f) != iLang)
+			break;
+
+        packetsize = (buff[buff[0x16] + 0x18] << 8) + buff[buff[0x16] + 0x19];
+		datasize = (buff[buff[0x16] + 0x1a] << 8) + buff[buff[0x16] + 0x1b];
+
+		ret = new BYTE[packetsize];
+		if(!ret) break;
+
+		int i = 0, sizeleft = packetsize;
+        for(int size; 
+			i < packetsize; 
+			i += size, sizeleft -= size)
+		{
+			int hsize = 0x18 + buff[0x16];
+			size = min(sizeleft, 0x800 - hsize);
+			memcpy(&ret[i], &buff[hsize], size);
+
+            if(size != sizeleft) 
+			{
+				while(m_sub.Read(buff, sizeof(buff)))
+				{
+					if(/*!(buff[0x15] & 0x80) &&*/ buff[buff[0x16] + 0x17] == (iLang|0x20)) 
+						break;
+				}
+			}
+		}
+
+		if(i != packetsize || sizeleft > 0)
+			delete [] ret, ret = NULL;
+	}
+	while(false);
+
+	return(ret);
+}
+
+bool CVobSubFile::GetFrame(int idx, int iLang)
+{
+	if(iLang < 0 || iLang >= 32) iLang = m_iLang;
+	CAtlArray<SubPos>& sp = m_langs[iLang].subpos;
+
+	if(idx < 0 || idx >= sp.GetCount())
+		return(false);
+
+	if(m_img.iLang != iLang || m_img.iIdx != idx) 
+	{
+		int packetsize = 0, datasize = 0;
+		CAutoVectorPtr<BYTE> buff;
+		buff.Attach(GetPacket(idx, packetsize, datasize, iLang));
+		if(!buff || packetsize <= 0 || datasize <= 0) return(false);
+
+		m_img.start = sp[idx].start;
+		m_img.delay = idx < (sp.GetCount()-1)
+			? sp[idx+1].start - sp[idx].start
+			: 3000;
+
+		bool ret = m_img.Decode(buff, packetsize, datasize, m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
+		
+		if(idx < (sp.GetCount()-1))
+			m_img.delay = min(m_img.delay, sp[idx+1].start - m_img.start);
+
+		if(!ret) return(false);
+		
+		m_img.iIdx = idx;
+		m_img.iLang = iLang;
+	}
+
+	return(m_fOnlyShowForcedSubs ? m_img.fForced : true);
+}
+
+bool CVobSubFile::GetFrameByTimeStamp(__int64 time)
+{
+	return(GetFrame(GetFrameIdxByTimeStamp(time)));
+}
+
+int CVobSubFile::GetFrameIdxByTimeStamp(__int64 time)
+{
+	if(m_iLang < 0 || m_iLang >= 32)
+		return(-1);
+
+	CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
+
+	int i = 0, j = (int)sp.GetCount() - 1, ret = -1;
+
+	if(j >= 0 && time >= sp[j].start)
+		return(j);
+
+	while(i < j)
+	{
+		int mid = (i + j) >> 1;
+		int midstart = (int)sp[mid].start;
+
+		if(time == midstart) {ret = mid; break;}
+		else if(time < midstart) {ret = -1; if(j == mid) mid--; j = mid;}
+		else if(time > midstart) {ret = mid; if(i == mid) mid++; i = mid;}
+	}
+
+	return(ret);
+}
+
+//
+
+STDMETHODIMP CVobSubFile::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+    CheckPointer(ppv, E_POINTER);
+    *ppv = NULL;
+
+    return 
+		QI(IPersist)
+		QI(ISubStream)
+		QI(ISubPicProvider)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicProvider
+
+// TODO: return segments for the fade-in/out time (with animated set to "true" of course)
+
+STDMETHODIMP_(POSITION) CVobSubFile::GetStartPosition(REFERENCE_TIME rt, double fps)
+{
+	rt /= 10000;
+
+	int i = GetFrameIdxByTimeStamp(rt);
+
+	if(!GetFrame(i))
+		return(NULL);
+
+	if(rt >= (m_img.start + m_img.delay))
+	{
+		if(!GetFrame(++i))
+			return(NULL);
+	}
+
+	return((POSITION)(i+1));
+}
+
+STDMETHODIMP_(POSITION) CVobSubFile::GetNext(POSITION pos)
+{
+	int i = (int)pos;
+	return(GetFrame(i) ? (POSITION)(i+1) : NULL);
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStart(POSITION pos, double fps)
+{
+	int i = (int)pos-1;
+	return(GetFrame(i) ? 10000i64*m_img.start : 0);
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CVobSubFile::GetStop(POSITION pos, double fps)
+{
+	int i = (int)pos-1;
+	return(GetFrame(i) ? 10000i64*(m_img.start + m_img.delay) : 0);
+}
+
+STDMETHODIMP_(bool) CVobSubFile::IsAnimated(POSITION pos)
+{
+	return(false);
+}
+
+STDMETHODIMP CVobSubFile::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
+{
+	if(spd.bpp != 32) return E_INVALIDARG;
+
+	rt /= 10000;
+
+	if(!GetFrame(GetFrameIdxByTimeStamp(rt)))
+		return E_FAIL;
+
+	if(rt >= (m_img.start + m_img.delay))
+		return E_FAIL;
+
+	return __super::Render(spd, bbox);
+}
+
+// IPersist
+
+STDMETHODIMP CVobSubFile::GetClassID(CLSID* pClassID)
+{
+	return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
+}
+
+// ISubStream
+
+STDMETHODIMP_(int) CVobSubFile::GetStreamCount()
+{
+	int iStreamCount = 0;
+	for(int i = 0; i < 32; i++)
+		if(m_langs[i].subpos.GetCount()) iStreamCount++;
+	return(iStreamCount);
+}
+
+STDMETHODIMP CVobSubFile::GetStreamInfo(int iStream, WCHAR** ppName, LCID* pLCID)
+{
+	for(int i = 0; i < 32; i++)
+	{
+		SubLang& sl = m_langs[i];
+		
+		if(sl.subpos.IsEmpty() || iStream-- > 0)
+			continue;
+
+		if(ppName)
+		{
+			if(!(*ppName = (WCHAR*)CoTaskMemAlloc((sl.alt.GetLength()+1)*sizeof(WCHAR))))
+				return E_OUTOFMEMORY;
+
+			wcscpy(*ppName, CStringW(sl.alt));
+		}
+
+		if(pLCID)
+		{
+			*pLCID = 0; // TODO: make lcid out of "sl.id"
+		}
+
+		return S_OK;
+	}
+
+	return E_FAIL;
+}
+
+STDMETHODIMP_(int) CVobSubFile::GetStream()
+{
+	int iStream = 0;
+
+	for(int i = 0; i < m_iLang; i++)
+		if(!m_langs[i].subpos.IsEmpty()) iStream++;
+
+	return(iStream);
+}
+
+STDMETHODIMP CVobSubFile::SetStream(int iStream)
+{
+	for(int i = 0; i < 32; i++)
+	{
+		CAtlArray<SubPos>& sp = m_langs[i].subpos;
+
+		if(sp.IsEmpty() || iStream-- > 0)
+			continue;
+
+		m_iLang = i;
+
+		m_img.Invalidate();
+
+		break;
+	}
+
+	return iStream < 0 ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP CVobSubFile::Reload()
+{
+	CFileStatus s;
+	if(!CFile::GetStatus(m_title + _T(".idx"), s)) return E_FAIL;
+	return !m_title.IsEmpty() && Open(m_title) ? S_OK : E_FAIL;
+}
+
+// StretchBlt
+
+static void PixelAtBiLinear(RGBQUAD& c, int x, int y, CVobSubImage& src)
+{
+	int w = src.rect.Width(),
+		h = src.rect.Height();
+
+	int x1 = (x >> 16), y1 = (y >> 16) * w,
+		x2 = min(x1 + 1, w-1), y2 = min(y1 + w, (h-1)*w);
+
+	RGBQUAD* ptr = src.lpPixels;
+
+	RGBQUAD c11 = ptr[y1 + x1],	c12 = ptr[y1 + x2],
+			c21 = ptr[y2 + x1],	c22 = ptr[y2 + x2];
+
+	__int64 u2 = x & 0xffff,
+			v2 = y & 0xffff,
+			u1 = 0x10000 - u2,
+			v1 = 0x10000 - v2;
+
+	int v1u1 = int(v1*u1 >> 16) * c11.rgbReserved, 
+		v1u2 = int(v1*u2 >> 16) * c12.rgbReserved,
+		v2u1 = int(v2*u1 >> 16) * c21.rgbReserved, 
+		v2u2 = int(v2*u2 >> 16) * c22.rgbReserved;
+
+	c.rgbRed = (c11.rgbRed * v1u1 + c12.rgbRed * v1u2
+			  + c21.rgbRed * v2u1 + c22.rgbRed * v2u2) >> 24;
+	c.rgbGreen = (c11.rgbGreen * v1u1 + c12.rgbGreen * v1u2
+				+ c21.rgbGreen * v2u1 + c22.rgbGreen * v2u2) >> 24;
+	c.rgbBlue = (c11.rgbBlue * v1u1 + c12.rgbBlue * v1u2
+				+ c21.rgbBlue * v2u1 + c22.rgbBlue * v2u2) >> 24;
+	c.rgbReserved = (v1u1 + v1u2 
+					+ v2u1 + v2u2) >> 16;
+}
+
+static void StretchBlt(SubPicDesc& spd, CRect dstrect, CVobSubImage& src)
+{
+	if(dstrect.IsRectEmpty()) return;
+
+	if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
+
+	int sw = src.rect.Width(),
+		sh = src.rect.Height(),
+		dw = dstrect.Width(),
+		dh = dstrect.Height();
+
+	int srcx = 0, 
+		srcy = 0,
+		srcdx = (sw << 16) / dw >> 1, 
+		srcdy = (sh << 16) / dh >> 1;
+
+	if(dstrect.left < 0) {srcx = -dstrect.left * (srcdx<<1); dstrect.left = 0;}
+	if(dstrect.top < 0) {srcy = -dstrect.top * (srcdy<<1); dstrect.top = 0;}
+	if(dstrect.right > spd.w) {dstrect.right = spd.w;}
+	if(dstrect.bottom > spd.h) {dstrect.bottom = spd.h;}
+
+	if((dstrect & CRect(0, 0, spd.w, spd.h)).IsRectEmpty()) return;
+
+	dw = dstrect.Width();
+	dh = dstrect.Height();
+
+	for(int y = dstrect.top; y < dstrect.bottom; y++, srcy += (srcdy<<1))
+	{
+		RGBQUAD* ptr = (RGBQUAD*)&((BYTE*)spd.bits)[y*spd.pitch] + dstrect.left;
+		RGBQUAD* endptr = ptr + dw;
+		
+		for(int sx = srcx; ptr < endptr; sx += (srcdx<<1), ptr++)
+		{
+//			PixelAtBiLinear(*ptr,	sx,			srcy,		src);
+////
+			RGBQUAD cc[4];
+
+			PixelAtBiLinear(cc[0],	sx,			srcy,		src);
+			PixelAtBiLinear(cc[1],	sx+srcdx,	srcy,		src);
+			PixelAtBiLinear(cc[2],	sx,			srcy+srcdy,	src);
+			PixelAtBiLinear(cc[3],	sx+srcdx,	srcy+srcdy,	src);
+			
+			ptr->rgbRed = (cc[0].rgbRed + cc[1].rgbRed + cc[2].rgbRed + cc[3].rgbRed) >> 2;
+			ptr->rgbGreen = (cc[0].rgbGreen + cc[1].rgbGreen + cc[2].rgbGreen + cc[3].rgbGreen) >> 2;
+			ptr->rgbBlue = (cc[0].rgbBlue + cc[1].rgbBlue + cc[2].rgbBlue + cc[3].rgbBlue) >> 2;
+			ptr->rgbReserved = (cc[0].rgbReserved + cc[1].rgbReserved + cc[2].rgbReserved + cc[3].rgbReserved) >> 2;
+////
+			ptr->rgbRed = ptr->rgbRed * ptr->rgbReserved >> 8;
+			ptr->rgbGreen = ptr->rgbGreen * ptr->rgbReserved >> 8;
+			ptr->rgbBlue = ptr->rgbBlue * ptr->rgbReserved >> 8;
+			ptr->rgbReserved = ~ptr->rgbReserved;
+
+		}
+	}
+}
+
+//
+// CVobSubSettings
+//
+
+void CVobSubSettings::InitSettings()
+{
+	m_size.SetSize(720, 480);
+	m_toff = m_x = m_y = 0;
+	m_org.SetPoint(0, 0);
+	m_scale_x = m_scale_y = m_alpha = 100;
+	m_fadein = m_fadeout = 50;
+	m_fSmooth = 0;
+	m_fAlign = false;
+	m_alignhor = m_alignver = 0;
+	m_fOnlyShowForcedSubs = false;
+	m_fCustomPal = false;
+	m_tridx = 0;
+	memset(m_orgpal, 0, sizeof(m_orgpal));
+	memset(m_cuspal, 0, sizeof(m_cuspal));
+}
+
+bool CVobSubSettings::GetCustomPal(RGBQUAD* cuspal, int& tridx)
+{
+	memcpy(cuspal, m_cuspal, sizeof(RGBQUAD)*4); 
+	tridx = m_tridx;
+	return(m_fCustomPal);
+}
+
+void CVobSubSettings::SetCustomPal(RGBQUAD* cuspal, int tridx) 
+{
+	memcpy(m_cuspal, cuspal, sizeof(RGBQUAD)*4);
+	m_tridx = tridx & 0xf;
+	for(int i = 0; i < 4; i++) m_cuspal[i].rgbReserved = (tridx&(1<<i)) ? 0 : 0xff;
+	m_img.Invalidate();
+}
+
+void CVobSubSettings::GetDestrect(CRect& r)
+{
+	int w = MulDiv(m_img.rect.Width(), m_scale_x, 100);
+	int h = MulDiv(m_img.rect.Height(), m_scale_y, 100);
+
+	if(!m_fAlign)
+	{
+		r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
+		r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
+		r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
+		r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
+	}
+	else
+	{
+		switch(m_alignhor)
+		{
+		case 0: r.left = 0; r.right = w; break; // left
+		case 1: r.left = -(w>>1); r.right = -(w>>1) + w; break; // center
+		case 2: r.left = -w; r.right = 0; break; // right
+		default:
+			r.left = MulDiv(m_img.rect.left, m_scale_x, 100);
+			r.right = MulDiv(m_img.rect.right, m_scale_x, 100);
+			break;
+		}
+		
+		switch(m_alignver)
+		{
+		case 0: r.top = 0; r.bottom = h; break; // top
+		case 1: r.top = -(h>>1); r.bottom = -(h>>1) + h; break; // center
+		case 2: r.top = -h; r.bottom = 0; break; // bottom
+		default:
+			r.top = MulDiv(m_img.rect.top, m_scale_y, 100);
+			r.bottom = MulDiv(m_img.rect.bottom, m_scale_y, 100);
+			break;
+		}
+	}
+
+	r += m_org;
+}
+
+void CVobSubSettings::GetDestrect(CRect& r, int w, int h)
+{
+	GetDestrect(r);
+
+	r.left = MulDiv(r.left, w, m_size.cx);
+	r.right = MulDiv(r.right, w, m_size.cx);
+	r.top = MulDiv(r.top, h, m_size.cy);
+	r.bottom = MulDiv(r.bottom, h, m_size.cy);
+}
+
+void CVobSubSettings::SetAlignment(bool fAlign, int x, int y, int hor, int ver)
+{
+	if(m_fAlign = fAlign)
+	{
+		m_org.x = MulDiv(m_size.cx, x, 100);
+		m_org.y = MulDiv(m_size.cy, y, 100);
+		m_alignhor = min(max(hor, 0), 2);
+		m_alignver = min(max(ver, 0), 2);
+	}
+	else
+	{
+		m_org.x = m_x;
+		m_org.y = m_y;
+	}
+}
+
+#include "RTS.h"
+
+HRESULT CVobSubSettings::Render(SubPicDesc& spd, RECT& bbox)
+{
+	CRect r;
+	GetDestrect(r, spd.w, spd.h);
+	StretchBlt(spd, r, m_img);
+/*
+CRenderedTextSubtitle rts(NULL);
+rts.CreateDefaultStyle(DEFAULT_CHARSET);
+rts.m_dstScreenSize.SetSize(m_size.cx, m_size.cy);
+CStringW assstr;
+m_img.Polygonize(assstr, false);
+REFERENCE_TIME rtStart = 10000i64*m_img.start, rtStop = 10000i64*(m_img.start+m_img.delay);
+rts.Add(assstr, true, rtStart, rtStop);
+rts.Render(spd, (rtStart+rtStop)/2, 25, r);
+*/
+	r &= CRect(CPoint(0, 0), CSize(spd.w, spd.h));
+	bbox = r;
+	return !r.IsRectEmpty() ? S_OK : S_FALSE;
+}
+
+/////////////////////////////////////////////////////////
+
+static bool CompressFile(CString fn)
+{
+	if(GetVersion() < 0)
+		return(false);
+
+	BOOL b = FALSE;
+
+	HANDLE h = CreateFile(fn, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
+	if(h != INVALID_HANDLE_VALUE)
+	{
+		USHORT us = COMPRESSION_FORMAT_DEFAULT;
+		DWORD nBytesReturned;
+		b = DeviceIoControl(h, FSCTL_SET_COMPRESSION, (LPVOID)&us, 2, NULL, 0, (LPDWORD)&nBytesReturned, NULL);
+		CloseHandle(h);
+	}
+
+	return(!!b);
+}
+
+bool CVobSubFile::SaveVobSub(CString fn)
+{
+	return WriteIdx(fn + _T(".idx")) && WriteSub(fn + _T(".sub"));
+}
+
+bool CVobSubFile::SaveWinSubMux(CString fn)
+{
+	TrimExtension(fn);
+
+	CStdioFile f;
+	if(!f.Open(fn + _T(".sub"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite)) 
+		return(false);
+
+	m_img.Invalidate();
+
+	CAutoVectorPtr<BYTE> p4bpp;
+	if(!p4bpp.Allocate(720*576/2))
+		return(false);
+
+	CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
+	for(int i = 0; i < sp.GetCount(); i++)
+	{
+		if(!GetFrame(i)) continue;
+
+		int pal[4] = {0, 1, 2, 3};
+
+		for(int j = 0; j < 5; j++)
+		{
+			if(j == 4 || !m_img.pal[j].tr)
+			{
+				j &= 3;
+				memset(p4bpp, (j<<4)|j, 720*576/2);
+				pal[j] ^= pal[0], pal[0] ^= pal[j], pal[j] ^= pal[0];
+				break;
+			}
+		}
+
+		int tr[4] = {m_img.pal[pal[0]].tr, m_img.pal[pal[1]].tr, m_img.pal[pal[2]].tr, m_img.pal[pal[3]].tr};
+
+		DWORD uipal[4+12];
+
+		if(!m_fCustomPal)
+		{
+			uipal[0] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[0]].pal]);
+			uipal[1] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[1]].pal]);
+			uipal[2] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[2]].pal]);
+			uipal[3] = *((DWORD*)&m_img.orgpal[m_img.pal[pal[3]].pal]);
+		}
+		else
+		{
+			uipal[0] = *((DWORD*)&m_img.cuspal[pal[0]]) & 0xffffff;
+			uipal[1] = *((DWORD*)&m_img.cuspal[pal[1]]) & 0xffffff;
+			uipal[2] = *((DWORD*)&m_img.cuspal[pal[2]]) & 0xffffff;
+			uipal[3] = *((DWORD*)&m_img.cuspal[pal[3]]) & 0xffffff;
+		}
+
+		CAtlMap<DWORD,BYTE> palmap;
+		palmap[uipal[0]] = 0;
+		palmap[uipal[1]] = 1;
+		palmap[uipal[2]] = 2;
+		palmap[uipal[3]] = 3;
+
+		uipal[0] = 0xff; // blue background
+
+		int w = m_img.rect.Width()-2;
+		int h = m_img.rect.Height()-2;
+		int pitch = (((w+1)>>1) + 3) & ~3;
+
+		for(int y = 0; y < h; y++)
+		{
+			DWORD* p = (DWORD*)&m_img.lpPixels[(y+1)*(w+2)+1];
+
+			for(int x = 0; x < w; x++, p++)
+			{
+				BYTE c = 0;
+
+				if(*p & 0xff000000)
+				{
+					DWORD uic = *p & 0xffffff;
+					palmap.Lookup(uic, c);
+				}
+
+				BYTE& c4bpp = p4bpp[(h-y-1)*pitch+(x>>1)];
+				c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
+			}
+		}
+
+		int t1 = m_img.start, t2 = t1 + m_img.delay /*+ (m_size.cy==480?(1000/29.97+1):(1000/25))*/;
+
+		ASSERT(t2>t1);
+
+		if(t2 <= 0) continue;
+		if(t1 < 0) t1 = 0;
+
+		CString bmpfn;
+		bmpfn.Format(_T("%s_%06d.bmp"), fn, i+1);
+
+		CString str;
+		str.Format(_T("%s\t%02d:%02d:%02d:%02d %02d:%02d:%02d:%02d\t%03d %03d %03d %03d %d %d %d %d\n"), 
+			bmpfn,
+			t1/1000/60/60, (t1/1000/60)%60, (t1/1000)%60, (t1%1000)/10,
+			t2/1000/60/60, (t2/1000/60)%60, (t2/1000)%60, (t2%1000)/10,
+			m_img.rect.Width(), m_img.rect.Height(), m_img.rect.left, m_img.rect.top,
+			(tr[0]<<4)|tr[0], (tr[1]<<4)|tr[1], (tr[2]<<4)|tr[2], (tr[3]<<4)|tr[3]);
+		f.WriteString(str);
+
+		BITMAPFILEHEADER fhdr = 
+		{
+			0x4d42,
+			sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + pitch*h,
+			0, 0,
+			sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
+		};
+
+		BITMAPINFOHEADER ihdr = 
+		{
+			sizeof(BITMAPINFOHEADER),
+			w, h, 1, 4, 0,
+			0,
+			pitch*h, 0,
+			16, 4
+		};
+
+		CFile bmp;
+		if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
+		{
+			bmp.Write(&fhdr, sizeof(fhdr));
+			bmp.Write(&ihdr, sizeof(ihdr));
+			bmp.Write(uipal, sizeof(RGBQUAD)*16);
+			bmp.Write(p4bpp, pitch*h);
+			bmp.Close();
+
+			CompressFile(bmpfn);
+		}
+	}
+
+	return(true);
+}
+
+bool CVobSubFile::SaveScenarist(CString fn)
+{
+	TrimExtension(fn);
+
+	CStdioFile f;
+	if(!f.Open(fn + _T(".sst"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite)) 
+		return(false);
+
+	m_img.Invalidate();
+
+	fn.Replace('\\', '/');
+	CString title = fn.Mid(fn.ReverseFind('/')+1);
+
+	TCHAR buff[MAX_PATH], * pFilePart = buff;
+	if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
+		return(false);
+
+	CString fullpath = CString(buff).Left(pFilePart - buff);
+	fullpath.TrimRight(_T("\\/"));
+	if(fullpath.IsEmpty())
+		return(false);
+
+	CString str, str2;
+	str += _T("st_format\t2\n");
+	str += _T("Display_Start\t%s\n");
+	str += _T("TV_Type\t\t%s\n");
+	str += _T("Tape_Type\tNON_DROP\n");
+	str += _T("Pixel_Area\t(0 %d)\n");
+	str += _T("Directory\t%s\n");
+	str += _T("Subtitle\t%s\n");
+	str += _T("Display_Area\t(0 2 719 %d)\n");
+	str += _T("Contrast\t(15 15 15 0)\n");
+	str += _T("\n");
+	str += _T("PA\t(0 0 255 - - - )\n");
+	str += _T("E1\t(255 0 0 - - - )\n");
+	str += _T("E2\t(0 0 0 - - - )\n");
+	str += _T("BG\t(255 255 255 - - - )\n");
+	str += _T("\n");
+	str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
+	str2.Format(str, 
+		!m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
+		m_size.cy == 480 ? _T("NTSC") : _T("PAL"), 
+		m_size.cy-3,
+		fullpath,
+		title, 
+		m_size.cy == 480 ? 479 : 574);
+
+	f.WriteString(str2);
+
+	f.Flush();
+
+	RGBQUAD pal[16] = 
+	{
+		{255, 0, 0, 0},
+		{0, 0, 255, 0},
+		{0, 0, 0, 0},
+		{255, 255, 255, 0},
+		{0, 255, 0, 0},
+		{255, 0, 255, 0},
+		{0, 255, 255, 0},
+		{125, 125, 0, 0},
+		{125, 125, 125, 0},
+		{225, 225, 225, 0},
+		{0, 0, 125, 0},
+		{0, 125, 0, 0},
+		{125, 0, 0, 0},
+		{255, 0, 222, 0},
+		{0, 125, 222, 0},
+		{125, 0, 125, 0},
+	};
+
+	BITMAPFILEHEADER fhdr = 
+	{
+		0x4d42,
+		sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
+		0, 0,
+		sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
+	};
+
+	BITMAPINFOHEADER ihdr = 
+	{
+        sizeof(BITMAPINFOHEADER),
+		720, m_size.cy-2, 1, 4, 0,
+		360*(m_size.cy-2),
+		0, 0,
+		16, 4
+	};
+
+	bool fCustomPal = m_fCustomPal;
+	m_fCustomPal = true;
+	RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
+	memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
+	memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
+
+	CAutoVectorPtr<BYTE> p4bpp;
+	if(!p4bpp.Allocate((m_size.cy-2)*360))
+		return(false);
+
+	BYTE colormap[16];
+
+	for(int i = 0; i < 16; i++) 
+	{
+		int idx = 0, maxdif = 255*255*3+1;
+
+		for(int j = 0; j < 16 && maxdif; j++)
+		{
+			int rdif = pal[j].rgbRed - m_orgpal[i].rgbRed;
+			int gdif = pal[j].rgbGreen - m_orgpal[i].rgbGreen;
+			int bdif = pal[j].rgbBlue - m_orgpal[i].rgbBlue;
+
+			int dif = rdif*rdif + gdif*gdif + bdif*bdif;
+			if(dif < maxdif) {maxdif = dif; idx = j;}
+		}
+
+		colormap[i] = idx+1;
+	}
+
+	int pc[4] = {1, 1, 1, 1}, pa[4] = {15, 15, 15, 0};
+
+	CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
+	for(int i = 0, k = 0; i < sp.GetCount(); i++)
+	{
+		if(!GetFrame(i)) continue;
+
+		for(int j = 0; j < 5; j++)
+		{
+			if(j == 4 || !m_img.pal[j].tr)
+			{
+				j &= 3;
+				memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
+				break;
+			}
+		}
+
+		for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
+		{
+			ASSERT(m_size.cy-y-1 >= 0);
+			if(m_size.cy-y-1 < 0) break;
+
+			DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
+
+			for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
+			{
+				DWORD rgb = *p&0xffffff;
+				BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
+				BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
+				c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
+			}
+		}
+
+		CString bmpfn;
+		bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
+		title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
+
+		// E1, E2, P, Bg
+		int c[4] = {colormap[m_img.pal[1].pal], colormap[m_img.pal[2].pal], colormap[m_img.pal[0].pal], colormap[m_img.pal[3].pal]};
+		c[0]^=c[1], c[1]^=c[0], c[0]^=c[1];
+
+		if(memcmp(pc, c, sizeof(c)))
+		{
+			memcpy(pc, c, sizeof(c));
+			str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
+			f.WriteString(str);
+		}
+
+		// E1, E2, P, Bg
+		int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
+		a[0]^=a[1], a[1]^=a[0], a[0]^=a[1];
+
+		if(memcmp(pa, a, sizeof(a)))
+		{
+			memcpy(pa, a, sizeof(a));
+			str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
+			f.WriteString(str);
+		}
+
+		int t1 = sp[i].start;
+		int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
+		int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
+
+		int t2 = sp[i].stop;
+		int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
+		int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
+
+		if(t2 <= 0) continue;
+		if(t1 < 0) t1 = 0;
+
+		if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
+		{
+			f2++;
+			if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
+		}
+
+		if(i < sp.GetCount()-1)
+		{
+			int t3 = sp[i+1].start;
+			int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
+			int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
+
+			if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2) 
+			{
+				f2--;
+				if(f2 == -1) {f2 = (m_size.cy==480?29:24); s2--; if(s2 == -1) {s2 = 59; m2--; if(m2 == -1) {m2 = 59; if(h2 > 0) h2--;}}}
+			}
+		}
+
+		if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
+			continue;
+
+		str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
+			++k,
+			h1, m1, s1, f1,
+			h2, m2, s2, f2,
+			title);
+		f.WriteString(str);
+
+		CFile bmp;
+		if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::modeRead|CFile::typeBinary))
+		{
+			bmp.Write(&fhdr, sizeof(fhdr));
+			bmp.Write(&ihdr, sizeof(ihdr));
+			bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
+			bmp.Write(p4bpp, 360*(m_size.cy-2));
+			bmp.Close();
+
+			CompressFile(bmpfn);
+		}
+	}
+
+	m_fCustomPal = fCustomPal;
+	memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
+
+	return(true);
+}
+
+bool CVobSubFile::SaveMaestro(CString fn)
+{
+	TrimExtension(fn);
+
+	CStdioFile f;
+	if(!f.Open(fn + _T(".son"), CFile::modeCreate|CFile::modeWrite|CFile::typeText|CFile::shareDenyWrite)) 
+		return(false);
+
+	m_img.Invalidate();
+
+	fn.Replace('\\', '/');
+	CString title = fn.Mid(fn.ReverseFind('/')+1);
+
+	TCHAR buff[MAX_PATH], * pFilePart = buff;
+	if(GetFullPathName(fn, MAX_PATH, buff, &pFilePart) == 0)
+		return(false);
+
+	CString fullpath = CString(buff).Left(pFilePart - buff);
+	fullpath.TrimRight(_T("\\/"));
+	if(fullpath.IsEmpty())
+		return(false);
+
+	CString str, str2;
+	str += _T("st_format\t2\n");
+	str += _T("Display_Start\t%s\n");
+	str += _T("TV_Type\t\t%s\n");
+	str += _T("Tape_Type\tNON_DROP\n");
+	str += _T("Pixel_Area\t(0 %d)\n");
+	str += _T("Directory\t%s\n");
+	str += _T("Subtitle\t%s\n");
+	str += _T("Display_Area\t(0 2 719 %d)\n");
+	str += _T("Contrast\t(15 15 15 0)\n");
+	str += _T("\n");
+	str += _T("SP_NUMBER\tSTART\tEND\tFILE_NAME\n");
+	str2.Format(str, 
+		!m_fOnlyShowForcedSubs ? _T("non_forced") : _T("forced"),
+		m_size.cy == 480 ? _T("NTSC") : _T("PAL"), 
+		m_size.cy-3, 
+		fullpath,
+		title, 
+		m_size.cy == 480 ? 479 : 574);
+
+	f.WriteString(str2);
+
+	f.Flush();
+
+	RGBQUAD pal[16] = 
+	{
+		{255, 0, 0, 0},
+		{0, 0, 255, 0},
+		{0, 0, 0, 0},
+		{255, 255, 255, 0},
+		{0, 255, 0, 0},
+		{255, 0, 255, 0},
+		{0, 255, 255, 0},
+		{125, 125, 0, 0},
+		{125, 125, 125, 0},
+		{225, 225, 225, 0},
+		{0, 0, 125, 0},
+		{0, 125, 0, 0},
+		{125, 0, 0, 0},
+		{255, 0, 222, 0},
+		{0, 125, 222, 0},
+		{125, 0, 125, 0},
+	};
+
+	BITMAPFILEHEADER fhdr = 
+	{
+		0x4d42,
+		sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD) + 360*(m_size.cy-2),
+		0, 0,
+		sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 16*sizeof(RGBQUAD)
+	};
+
+	BITMAPINFOHEADER ihdr = 
+	{
+        sizeof(BITMAPINFOHEADER),
+		720, m_size.cy-2, 1, 4, 0,
+		360*(m_size.cy-2),
+		0, 0,
+		16, 4
+	};
+
+	bool fCustomPal = m_fCustomPal;
+	m_fCustomPal = true;
+	RGBQUAD tempCusPal[4], newCusPal[4+12] = {{255, 0, 0, 0}, {0, 0, 255, 0}, {0, 0, 0, 0}, {255, 255, 255, 0}};
+	memcpy(tempCusPal, m_cuspal, sizeof(tempCusPal));
+	memcpy(m_cuspal, newCusPal, sizeof(m_cuspal));
+
+	CAutoVectorPtr<BYTE> p4bpp;
+	if(!p4bpp.Allocate((m_size.cy-2)*360))
+		return(false);
+
+	BYTE colormap[16];
+	for(int i = 0; i < 16; i++)
+		colormap[i] = i;
+
+	CFile spf;
+	if(spf.Open(fn + _T(".spf"), CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
+	{
+		for(int i = 0; i < 16; i++) 
+		{
+			COLORREF c = (m_orgpal[i].rgbBlue<<16) | (m_orgpal[i].rgbGreen<<8) | m_orgpal[i].rgbRed;
+			spf.Write(&c, sizeof(COLORREF));
+		}
+
+		spf.Close();
+	}
+
+	int pc[4] = {1,1,1,1}, pa[4] = {15,15,15,0};
+
+	CAtlArray<SubPos>& sp = m_langs[m_iLang].subpos;
+	for(int i = 0, k = 0; i < sp.GetCount(); i++)
+	{
+		if(!GetFrame(i)) continue;
+
+		for(int j = 0; j < 5; j++)
+		{
+			if(j == 4 || !m_img.pal[j].tr)
+			{
+				j &= 3;
+				memset(p4bpp, (j<<4)|j, (m_size.cy-2)*360);
+				break;
+			}
+		}
+
+		for(int y = max(m_img.rect.top+1, 2); y < m_img.rect.bottom-1; y++)
+		{
+			ASSERT(m_size.cy-y-1 >= 0);
+			if(m_size.cy-y-1 < 0) break;
+
+			DWORD* p = (DWORD*)&m_img.lpPixels[(y-m_img.rect.top)*m_img.rect.Width()+1];
+
+			for(int x = m_img.rect.left+1; x < m_img.rect.right-1; x++, p++)
+			{
+				DWORD rgb = *p&0xffffff;
+				BYTE c = rgb == 0x0000ff ? 0 : rgb == 0xff0000 ? 1 : rgb == 0x000000 ? 2 : 3;
+				BYTE& c4bpp = p4bpp[(m_size.cy-y-1)*360+(x>>1)];
+				c4bpp = (x&1) ? ((c4bpp&0xf0)|c) : ((c4bpp&0x0f)|(c<<4));
+			}
+		}
+
+		CString bmpfn;
+		bmpfn.Format(_T("%s_%04d.bmp"), fn, i+1);
+		title = bmpfn.Mid(bmpfn.ReverseFind('/')+1);
+
+		// E1, E2, P, Bg
+		int c[4] = {colormap[m_img.pal[1].pal], colormap[m_img.pal[2].pal], colormap[m_img.pal[0].pal], colormap[m_img.pal[3].pal]};
+
+		if(memcmp(pc, c, sizeof(c)))
+		{
+			memcpy(pc, c, sizeof(c));
+			str.Format(_T("Color\t (%d %d %d %d)\n"), c[0], c[1], c[2], c[3]);
+			f.WriteString(str);
+		}
+
+		// E1, E2, P, Bg
+		int a[4] = {m_img.pal[1].tr, m_img.pal[2].tr, m_img.pal[0].tr, m_img.pal[3].tr};
+
+		if(memcmp(pa, a, sizeof(a)))
+		{
+			memcpy(pa, a, sizeof(a));
+			str.Format(_T("Contrast (%d %d %d %d)\n"), a[0], a[1], a[2], a[3]);
+			f.WriteString(str);
+		}
+
+		int t1 = sp[i].start;
+		int h1 = t1/1000/60/60, m1 = (t1/1000/60)%60, s1 = (t1/1000)%60;
+		int f1 = (int)((m_size.cy==480?29.97:25)*(t1%1000)/1000);
+
+		int t2 = sp[i].stop;
+		int h2 = t2/1000/60/60, m2 = (t2/1000/60)%60, s2 = (t2/1000)%60;
+		int f2 = (int)((m_size.cy==480?29.97:25)*(t2%1000)/1000);
+
+		if(t2 <= 0) continue;
+		if(t1 < 0) t1 = 0;
+
+		if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
+		{
+			f2++;
+			if(f2 == (m_size.cy==480?30:25)) {f2 = 0; s2++; if(s2 == 60) {s2 = 0; m2++; if(m2 == 60) {m2 = 0; h2++;}}}
+		}
+
+		if(i < sp.GetCount()-1)
+		{
+			int t3 = sp[i+1].start;
+			int h3 = t3/1000/60/60, m3 = (t3/1000/60)%60, s3 = (t3/1000)%60;
+			int f3 = (int)((m_size.cy==480?29.97:25)*(t3%1000)/1000);
+
+			if(h3 == h2 && m3 == m2 && s3 == s2 && f3 == f2)
+			{
+				f2--;
+				if(f2 == -1) {f2 = (m_size.cy==480?29:24); s2--; if(s2 == -1) {s2 = 59; m2--; if(m2 == -1) {m2 = 59; if(h2 > 0) h2--;}}}
+			}
+		}
+
+		if(h1 == h2 && m1 == m2 && s1 == s2 && f1 == f2)
+			continue;
+
+		str.Format(_T("%04d\t%02d:%02d:%02d:%02d\t%02d:%02d:%02d:%02d\t%s\n"),
+			++k,
+			h1, m1, s1, f1,
+			h2, m2, s2, f2,
+			title);
+		f.WriteString(str);
+
+		CFile bmp;
+		if(bmp.Open(bmpfn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary))
+		{
+			bmp.Write(&fhdr, sizeof(fhdr));
+			bmp.Write(&ihdr, sizeof(ihdr));
+			bmp.Write(newCusPal, sizeof(RGBQUAD)*16);
+			bmp.Write(p4bpp, 360*(m_size.cy-2));
+			bmp.Close();
+
+			CompressFile(bmpfn);
+		}
+	}
+
+	m_fCustomPal = fCustomPal;
+	memcpy(m_cuspal, tempCusPal, sizeof(m_cuspal));
+
+	return(true);
+}
+
+//
+// CVobSubStream
+//
+
+CVobSubStream::CVobSubStream(CCritSec* pLock)
+	: ISubPicProviderImpl(pLock)
+{
+}
+
+CVobSubStream::~CVobSubStream()
+{
+}
+
+void CVobSubStream::Open(CString name, BYTE* pData, int len)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+
+	m_name = name;
+
+	CAtlList<CString> lines;
+	Explode(CString(CStringA((CHAR*)pData, len)), lines, '\n');
+	while(lines.GetCount())
+	{
+		CAtlList<CString> sl;
+		Explode(lines.RemoveHead(), sl, ':', 2);
+		if(sl.GetCount() != 2) continue;
+		CString key = sl.GetHead();
+		CString value = sl.GetTail();
+		if(key == _T("size"))
+			_stscanf(value, _T("%dx %d"), &m_size.cx, &m_size.cy);
+		else if(key == _T("org"))
+			_stscanf(value, _T("%d, %d"), &m_org.x, &m_org.y);
+		else if(key == _T("scale"))
+			_stscanf(value, _T("%d%%, %d%%"), &m_scale_x, &m_scale_y);
+		else if(key == _T("alpha"))
+			_stscanf(value, _T("%d%%"), &m_alpha);
+		else if(key == _T("smooth"))
+			m_fSmooth = 
+				value == _T("0") || value == _T("OFF") ? 0 : 
+				value == _T("1") || value == _T("ON") ? 1 : 
+				value == _T("2") || value == _T("OLD") ? 2 : 
+				0;
+		else if(key == _T("align"))
+		{
+			Explode(value, sl, ' ');
+			if(sl.GetCount() == 4) sl.RemoveAt(sl.FindIndex(1));
+			if(sl.GetCount() == 3)
+			{
+				m_fAlign = sl.RemoveHead() == _T("ON");
+				CString hor = sl.GetHead(), ver = sl.GetTail();
+				m_alignhor = hor == _T("LEFT") ? 0 : hor == _T("CENTER") ? 1 : hor == _T("RIGHT") ? 2 : 1;
+				m_alignver = ver == _T("TOP") ? 0 : ver == _T("CENTER") ? 1 : ver == _T("BOTTOM") ? 2 : 2;
+			}
+		}
+		else if(key == _T("fade in/out"))
+			_stscanf(value, _T("%d%, %d%"), &m_fadein, &m_fadeout);
+		else if(key == _T("time offset"))
+			m_toff = _tcstol(value, NULL, 10);
+		else if(key == _T("forced subs"))
+			m_fOnlyShowForcedSubs = value == _T("1") || value == _T("ON");
+		else if(key == _T("palette"))
+		{
+			Explode(value, sl, ',', 16);
+			for(int i = 0; i < 16 && sl.GetCount(); i++)
+				*(DWORD*)&m_orgpal[i] = _tcstol(sl.RemoveHead(), NULL, 16);
+		}
+		else if(key == _T("custom colors"))
+		{
+			m_fCustomPal = Explode(value, sl, ',', 3) == _T("ON");
+			if(sl.GetCount() == 3)
+			{
+				sl.RemoveHead();
+				CAtlList<CString> tridx, colors;
+				Explode(sl.RemoveHead(), tridx, ':', 2);
+				if(tridx.RemoveHead() == _T("tridx"))
+				{
+					TCHAR tr[4];
+					_stscanf(tridx.RemoveHead(), _T("%c%c%c%c"), &tr[0], &tr[1], &tr[2], &tr[3]);
+					for(int i = 0; i < 4; i++)
+						m_tridx |= ((tr[i]=='1')?1:0)<<i;
+				}
+				Explode(sl.RemoveHead(), colors, ':', 2);
+				if(colors.RemoveHead() == _T("colors"))
+				{
+					Explode(colors.RemoveHead(), colors, ',', 4);
+					for(int i = 0; i < 4 && colors.GetCount(); i++)
+						*(DWORD*)&m_cuspal[i] = _tcstol(colors.RemoveHead(), NULL, 16);
+				}
+			}
+		}
+	}
+}
+
+void CVobSubStream::Add(REFERENCE_TIME tStart, REFERENCE_TIME tStop, BYTE* pData, int len)
+{
+	if(len <= 4 || ((pData[0]<<8)|pData[1]) != len) return;
+
+	CVobSubImage vsi;
+	vsi.GetPacketInfo(pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3]);
+
+	CAutoPtr<SubPic> p(new SubPic());
+	p->tStart = tStart;
+	p->tStop = vsi.delay > 0 ? (tStart + 10000i64*vsi.delay) : tStop;
+	p->pData.SetCount(len);
+	memcpy(p->pData.GetData(), pData, p->pData.GetCount());
+
+	CAutoLock cAutoLock(&m_csSubPics);
+	while(m_subpics.GetCount() && m_subpics.GetTail()->tStart >= tStart)
+		m_subpics.RemoveTail();
+	m_subpics.AddTail(p);
+}
+
+void CVobSubStream::RemoveAll()
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+	m_subpics.RemoveAll();
+}
+
+STDMETHODIMP CVobSubStream::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+    CheckPointer(ppv, E_POINTER);
+    *ppv = NULL;
+
+    return 
+		QI(IPersist)
+		QI(ISubStream)
+		QI(ISubPicProvider)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+// ISubPicProvider
+	
+STDMETHODIMP_(POSITION) CVobSubStream::GetStartPosition(REFERENCE_TIME rt, double fps)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+	POSITION pos = m_subpics.GetTailPosition();
+	for(; pos; m_subpics.GetPrev(pos))
+	{
+		SubPic* sp = m_subpics.GetAt(pos);
+		if(sp->tStart <= rt)
+		{
+			if(sp->tStop <= rt) m_subpics.GetNext(pos);
+			break;
+		}
+	}
+	return(pos);
+}
+
+STDMETHODIMP_(POSITION) CVobSubStream::GetNext(POSITION pos)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+	m_subpics.GetNext(pos);
+    return pos;
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStart(POSITION pos, double fps)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+	return m_subpics.GetAt(pos)->tStart;
+}
+
+STDMETHODIMP_(REFERENCE_TIME) CVobSubStream::GetStop(POSITION pos, double fps)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+	return m_subpics.GetAt(pos)->tStop;
+}
+
+STDMETHODIMP_(bool) CVobSubStream::IsAnimated(POSITION pos)
+{
+	return(false);
+}
+
+STDMETHODIMP CVobSubStream::Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox)
+{
+	if(spd.bpp != 32) return E_INVALIDARG;
+
+	POSITION pos = m_subpics.GetTailPosition();
+	for(; pos; m_subpics.GetPrev(pos))
+	{
+		SubPic* sp = m_subpics.GetAt(pos);
+		if(sp->tStart <= rt && rt < sp->tStop)
+		{
+			if(m_img.iIdx != (int)pos)
+			{
+				BYTE* pData = sp->pData.GetData();				
+				m_img.Decode(
+					pData, (pData[0]<<8)|pData[1], (pData[2]<<8)|pData[3],
+					m_fCustomPal, m_tridx, m_orgpal, m_cuspal, true);
+				m_img.iIdx = (int)pos;
+			}
+
+			return __super::Render(spd, bbox);
+		}
+	}
+
+	return E_FAIL;
+}
+
+// IPersist
+
+STDMETHODIMP CVobSubStream::GetClassID(CLSID* pClassID)
+{
+	return pClassID ? *pClassID = __uuidof(this), S_OK : E_POINTER;
+}
+
+// ISubStream
+
+STDMETHODIMP_(int) CVobSubStream::GetStreamCount()
+{
+	return 1;
+}
+
+STDMETHODIMP CVobSubStream::GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID)
+{
+	CAutoLock cAutoLock(&m_csSubPics);
+
+	if(ppName)
+	{
+		if(!(*ppName = (WCHAR*)CoTaskMemAlloc((m_name.GetLength()+1)*sizeof(WCHAR))))
+			return E_OUTOFMEMORY;
+		wcscpy(*ppName, CStringW(m_name));
+	}
+
+	if(pLCID)
+	{
+		*pLCID = 0; // TODO
+	}
+
+	return S_OK;
+}
+
+STDMETHODIMP_(int) CVobSubStream::GetStream()
+{
+	return 0;
+}
+
+STDMETHODIMP CVobSubStream::SetStream(int iStream)
+{
+	return iStream == 0 ? S_OK : E_FAIL;
+}
diff --git a/vsfilter/subtitles/VobSubFile.h b/vsfilter/subtitles/VobSubFile.h
new file mode 100644
index 0000000000000000000000000000000000000000..265e86ecf6f630f61e2493240e25e213e85c0eca
--- /dev/null
+++ b/vsfilter/subtitles/VobSubFile.h
@@ -0,0 +1,184 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include "VobSubImage.h"
+#include "..\SubPic\ISubPic.h"
+
+#define VOBSUBIDXVER 7
+
+extern CString FindLangFromId(WORD id);
+
+class CVobSubSettings
+{
+protected:
+	HRESULT Render(SubPicDesc& spd, RECT& bbox);
+
+public:
+	CSize m_size;
+	int m_x, m_y;
+	CPoint m_org;
+	int m_scale_x, m_scale_y;	// % (don't set it to unsigned because as a side effect it will mess up negative coordinates in GetDestrect())
+	int m_alpha;				// %
+	int m_fSmooth;				// 0: OFF, 1: ON, 2: OLD (means no filtering at all)
+	int m_fadein, m_fadeout;	// ms
+	bool m_fAlign;
+	int m_alignhor, m_alignver; // 0: left/top, 1: center, 2: right/bottom
+	unsigned int m_toff;				// ms
+	bool m_fOnlyShowForcedSubs;
+	bool m_fCustomPal;
+	int m_tridx;
+	RGBQUAD m_orgpal[16], m_cuspal[4];
+
+	CVobSubImage m_img;
+
+	CVobSubSettings() {InitSettings();}
+	void InitSettings();
+
+	bool GetCustomPal(RGBQUAD* cuspal, int& tridx);
+    void SetCustomPal(RGBQUAD* cuspal, int tridx);
+
+	void GetDestrect(CRect& r); // destrect of m_img, considering the current alignment mode
+	void GetDestrect(CRect& r, int w, int h); // this will scale it to the frame size of (w, h)
+
+	void SetAlignment(bool fAlign, int x, int y, int hor, int ver);
+};
+
+[uuid("998D4C9A-460F-4de6-BDCD-35AB24F94ADF")]
+class CVobSubFile : public CVobSubSettings, public ISubStream, public ISubPicProviderImpl
+{
+protected:
+	CString m_title;
+
+	void TrimExtension(CString& fn);
+	bool ReadIdx(CString fn, int& ver), ReadSub(CString fn), ReadRar(CString fn), ReadIfo(CString fn);
+	bool WriteIdx(CString fn), WriteSub(CString fn);
+
+	CMemFile m_sub;
+
+	BYTE* GetPacket(int idx, int& packetsize, int& datasize, int iLang = -1);
+	bool GetFrame(int idx, int iLang = -1);
+	bool GetFrameByTimeStamp(__int64 time);
+	int GetFrameIdxByTimeStamp(__int64 time);
+
+	bool SaveVobSub(CString fn);
+	bool SaveWinSubMux(CString fn);
+	bool SaveScenarist(CString fn);
+	bool SaveMaestro(CString fn);
+
+public:
+	typedef struct
+	{
+		__int64 filepos;
+		__int64 start, stop;
+		bool fForced;
+		char vobid, cellid;
+		__int64 celltimestamp;
+		bool fValid;
+	} SubPos;
+
+	typedef struct
+	{
+		int id;
+		CString name, alt;
+		CAtlArray<SubPos> subpos;
+	} SubLang;
+
+	int m_iLang;
+	SubLang m_langs[32];
+
+public:
+	CVobSubFile(CCritSec* pLock);
+	virtual ~CVobSubFile();
+
+	bool Copy(CVobSubFile& vsf);
+
+	typedef enum {None,VobSub,WinSubMux,Scenarist,Maestro} SubFormat;
+
+	bool Open(CString fn);
+	bool Save(CString fn, SubFormat sf = VobSub);
+	void Close();
+
+	CString GetTitle() {return(m_title);}
+
+	DECLARE_IUNKNOWN
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicProvider
+	STDMETHODIMP_(POSITION) GetStartPosition(REFERENCE_TIME rt, double fps);
+	STDMETHODIMP_(POSITION) GetNext(POSITION pos);
+	STDMETHODIMP_(REFERENCE_TIME) GetStart(POSITION pos, double fps);
+	STDMETHODIMP_(REFERENCE_TIME) GetStop(POSITION pos, double fps);
+	STDMETHODIMP_(bool) IsAnimated(POSITION pos);
+	STDMETHODIMP Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox);
+
+	// IPersist
+	STDMETHODIMP GetClassID(CLSID* pClassID);
+
+	// ISubStream
+	STDMETHODIMP_(int) GetStreamCount();
+	STDMETHODIMP GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID);
+	STDMETHODIMP_(int) GetStream();
+	STDMETHODIMP SetStream(int iStream);
+	STDMETHODIMP Reload();
+};
+
+[uuid("D7FBFB45-2D13-494F-9B3D-FFC9557D5C45")]
+class CVobSubStream : public CVobSubSettings, public ISubStream, public ISubPicProviderImpl
+{
+	CString m_name;
+
+	CCritSec m_csSubPics;
+	struct SubPic {REFERENCE_TIME tStart, tStop; CAtlArray<BYTE> pData;};
+	CAutoPtrList<SubPic> m_subpics;
+
+public:
+	CVobSubStream(CCritSec* pLock);
+	virtual ~CVobSubStream();
+
+	void Open(CString name, BYTE* pData, int len);
+
+    void Add(REFERENCE_TIME tStart, REFERENCE_TIME tStop, BYTE* pData, int len);
+	void RemoveAll();
+
+	DECLARE_IUNKNOWN
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// ISubPicProvider
+	STDMETHODIMP_(POSITION) GetStartPosition(REFERENCE_TIME rt, double fps);
+	STDMETHODIMP_(POSITION) GetNext(POSITION pos);
+	STDMETHODIMP_(REFERENCE_TIME) GetStart(POSITION pos, double fps);
+	STDMETHODIMP_(REFERENCE_TIME) GetStop(POSITION pos, double fps);
+	STDMETHODIMP_(bool) IsAnimated(POSITION pos);
+	STDMETHODIMP Render(SubPicDesc& spd, REFERENCE_TIME rt, double fps, RECT& bbox);
+
+	// IPersist
+	STDMETHODIMP GetClassID(CLSID* pClassID);
+
+	// ISubStream
+	STDMETHODIMP_(int) GetStreamCount();
+	STDMETHODIMP GetStreamInfo(int i, WCHAR** ppName, LCID* pLCID);
+	STDMETHODIMP_(int) GetStream();
+	STDMETHODIMP SetStream(int iStream);
+	STDMETHODIMP Reload() {return E_NOTIMPL;}
+};
diff --git a/vsfilter/subtitles/VobSubFileRipper.cpp b/vsfilter/subtitles/VobSubFileRipper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..76d40c4b77d9ee72e525125b26b86746adbc4f32
--- /dev/null
+++ b/vsfilter/subtitles/VobSubFileRipper.cpp
@@ -0,0 +1,1259 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "StdAfx.h"
+#include "vobsubfileripper.h"
+#include "../include/decss/VobDec.h"
+#include "../subtitles/CCDecoder.h"
+
+//
+// CVobSubFileRipper
+//
+
+CVobSubFileRipper::CVobSubFileRipper()
+	: CVobSubFile(NULL)
+	, m_fThreadActive(false)
+	, m_fBreakThread(false)
+	, m_fIndexing(false)
+{
+	m_rd.Reset();
+	CAMThread::Create();
+}
+
+CVobSubFileRipper::~CVobSubFileRipper()
+{
+	CAMThread::CallWorker(CMD_EXIT);
+	CAMThread::Close();
+}
+
+STDMETHODIMP CVobSubFileRipper::NonDelegatingQueryInterface(REFIID riid, void** ppv)
+{
+	return 
+		QI(IVSFRipper)
+		__super::NonDelegatingQueryInterface(riid, ppv);
+}
+
+void CVobSubFileRipper::Log(log_t type, LPCTSTR lpszFormat, ...)
+{
+	CAutoLock cAutoLock(&m_csCallback);
+	if(!m_pCallback) return;
+
+	TCHAR buff[1024];
+
+	va_list args;
+	va_start(args, lpszFormat);
+	_vstprintf(buff, lpszFormat, args);
+	va_end(args);
+
+	CString msg;
+	switch(type)
+	{
+	default:
+	case LOG_INFO: msg = _T(""); break;
+	case LOG_WARNING: msg = _T("WARNING: "); break;
+	case LOG_ERROR: msg = _T("ERROR: "); break;
+	}
+
+	msg += buff;
+
+	m_pCallback->OnMessage(msg);
+}
+
+void CVobSubFileRipper::Progress(double progress)
+{
+	CAutoLock cAutoLock(&m_csCallback);
+	if(!m_pCallback) return;
+
+	m_pCallback->OnProgress(progress);
+}
+
+void CVobSubFileRipper::Finished(bool fSucceeded)
+{
+	CAutoLock cAutoLock(&m_csCallback);
+	if(!m_pCallback) return;
+
+	m_pCallback->OnFinished(fSucceeded);
+}
+
+#define ReadBEb(var) \
+	f.Read(&((BYTE*)&var)[0], 1); \
+
+#define ReadBEw(var) \
+	f.Read(&((BYTE*)&var)[1], 1); \
+	f.Read(&((BYTE*)&var)[0], 1); \
+
+#define ReadBEdw(var) \
+    f.Read(&((BYTE*)&var)[3], 1); \
+	f.Read(&((BYTE*)&var)[2], 1); \
+	f.Read(&((BYTE*)&var)[1], 1); \
+	f.Read(&((BYTE*)&var)[0], 1); \
+
+bool CVobSubFileRipper::LoadIfo(CString fn)
+{
+	CString str;
+
+	CFileStatus status;
+	if(!CFileGetStatus(fn, status) || !status.m_size)
+	{
+		Log(LOG_ERROR, _T("Invalid ifo"));
+		return(false);
+	}
+
+	CFile f;
+	if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+	{
+		Log(LOG_ERROR, _T("Cannot open ifo"));
+		return(false);
+	}
+
+	Log(LOG_INFO, _T("Opening ifo OK"));
+
+	char hdr[13];
+	f.Read(hdr, 12);
+	hdr[12] = 0;
+	if(strcmp(hdr, "DVDVIDEO-VTS"))
+	{
+		Log(LOG_ERROR, _T("Not a Video Title Set IFO file!"));
+		return(false);
+	}
+
+	// lang ids
+
+	f.Seek(0x254, CFile::begin);
+
+	WORD ids[32];
+	memset(ids, 0, sizeof(ids));
+
+	int len = 0;
+	ReadBEw(len);
+
+	for(int i = 0; i < len; i++)
+	{
+		f.Seek(2, CFile::current); // 01 00 ?
+		ReadBEw(ids[i]);
+		if(ids[i] == 0) ids[i] = '--';
+		f.Seek(2, CFile::current); // 00 00 ?
+	}
+
+	/* Video info */
+
+	f.Seek(0x200, CFile::begin);
+	f.Read(&m_rd.vidinfo, 2);
+
+	SIZE res[4][2] =
+	{
+		{{720,480},{720,576}},
+		{{704,480},{704,576}},
+		{{352,480},{352,576}},
+		{{352,240},{352,288}}
+	};
+
+	m_rd.vidsize = res[m_rd.vidinfo.source_res][m_rd.vidinfo.system&1];
+
+	double rate = (m_rd.vidinfo.system == 0) ? 30.0/29.97 : 1.0;
+
+	/* PGCs */
+
+	{
+		DWORD offset;
+
+		DWORD pgcpos;
+		f.Seek(0xc0+0x0c, CFile::begin);
+		ReadBEdw(pgcpos);
+		pgcpos *= 0x800;
+
+		WORD nPGC;
+		f.Seek(pgcpos, CFile::begin);
+		ReadBEw(nPGC);
+
+		m_rd.pgcs.RemoveAll();
+		m_rd.pgcs.SetCount(nPGC);
+
+		for(int i = 0; i < nPGC; i++)
+		{
+			PGC& pgc = m_rd.pgcs[i];
+
+			f.Seek(pgcpos + 8 + i*8 + 4, CFile::begin);
+			ReadBEdw(offset);
+			offset += pgcpos;
+
+			BYTE nProgs, nCells;
+			f.Seek(offset + 2, CFile::begin);
+			ReadBEb(nProgs);
+			ReadBEb(nCells);
+
+			//
+
+			memcpy(pgc.ids, ids, sizeof(ids));
+
+			struct splanginfo {BYTE res1, id1, id2, res2;};
+			splanginfo splinfo[32];
+
+			f.Seek(offset + 0x1c, CFile::begin);
+			f.Read(splinfo, 32*4);
+
+			for(int j = 0; j < 32; j++) 
+			{
+				if(splinfo[j].id1 || splinfo[i].id2) 
+				{
+					WORD tmpids[32];
+					memset(tmpids, 0, sizeof(tmpids));
+
+					for(j = 0; j < 32; j++) 
+					{
+						if(!(splinfo[j].res1 & 0x80)) break;
+
+						pgc.ids[splinfo[j].id1] = ids[j];
+						pgc.ids[splinfo[j].id2] = ids[j];
+					}
+
+					break;
+				}
+			}
+
+			//
+
+			f.Seek(offset + 0xa4, CFile::begin);
+
+			for(int j = 0; j < 16; j++) 
+			{
+				BYTE y, u, v, tmp;
+
+				f.Read(&tmp, 1);
+				f.Read(&y, 1);
+				f.Read(&u, 1);
+				f.Read(&v, 1);
+
+				y = (y-16)*255/219;
+
+				pgc.pal[j].rgbRed = (BYTE)min(max(1.0*y + 1.4022*(u-128), 0), 255);
+				pgc.pal[j].rgbGreen = (BYTE)min(max(1.0*y - 0.3456*(u-128) - 0.7145*(v-128), 0), 255);
+				pgc.pal[j].rgbBlue = (BYTE)min(max(1.0*y + 1.7710*(v-128), 0) , 255);
+			}
+
+			//
+
+			WORD progoff, celladdroff, vobcelloff;
+			f.Seek(offset + 0xe6, CFile::begin);
+			ReadBEw(progoff);
+			f.Seek(offset + 0xe8, CFile::begin);
+			ReadBEw(celladdroff);
+			f.Seek(offset + 0xea, CFile::begin);
+			ReadBEw(vobcelloff);
+
+			//
+
+            CAtlArray<BYTE> progs;
+			progs.SetCount(nProgs);
+			f.Seek(offset + progoff, CFile::begin);
+			f.Read(progs.GetData(), nProgs);
+
+			//
+
+			pgc.angles[0].SetCount(nCells);
+			pgc.iSelAngle = 0;
+
+			//
+
+			f.Seek(offset + vobcelloff, CFile::begin);
+			for(int j = 0; j < nCells; j++)
+			{
+				ReadBEw(pgc.angles[0][j].vob);
+				ReadBEw(pgc.angles[0][j].cell);
+			}
+
+			//
+
+			DWORD tOffset = 0, tTotal = 0;
+
+			int iAngle = 0;
+
+			pgc.nAngles = 0;
+
+			f.Seek(offset + celladdroff, CFile::begin);
+			for(int j = 0; j < nCells; j++)
+			{
+				BYTE b;
+				ReadBEb(b);
+				switch(b>>6)
+				{
+				case 0: iAngle = 0; break; // normal
+				case 1: iAngle = 1; break; // first angle block
+				case 2: iAngle++; break; // middle angle block
+				case 3: iAngle++; break; // last angle block (no more should follow)
+				}
+				pgc.angles[0][j].iAngle = iAngle;
+				pgc.nAngles = max(pgc.nAngles, iAngle);
+
+				f.Seek(3, CFile::current);
+				ReadBEdw(pgc.angles[0][j].tTime);
+				ReadBEdw(pgc.angles[0][j].start);
+				f.Seek(8, CFile::current);
+				ReadBEdw(pgc.angles[0][j].end);
+
+				float fps;
+				switch((pgc.angles[0][j].tTime>>6)&0x3)
+				{
+				default:
+				case 3: fps = 30; break;
+				case 1: fps = 25; break;
+				}
+
+				int t = pgc.angles[0][j].tTime;
+				int hh = ((t>>28)&0xf)*10+((t>>24)&0xf);
+				int mm = ((t>>20)&0xf)*10+((t>>16)&0xf);
+				int ss = ((t>>12)&0xf)*10+((t>>8)&0xf);
+				int ms = (int)(1000.0 * (((t>>4)&0x3)*10+((t>>0)&0xf)) / fps);
+				pgc.angles[0][j].tTime = (DWORD)((((hh*60+mm)*60+ss)*1000+ms)*rate);
+
+				// time discontinuity
+				if(b&0x02) tOffset = tTotal;
+				pgc.angles[0][j].fDiscontinuity = !!(b&0x02);
+
+				pgc.angles[0][j].tTotal = tTotal;
+				pgc.angles[0][j].tOffset = tOffset;
+
+				tTotal += pgc.angles[0][j].tTime;
+			}
+
+			for(iAngle = 1; iAngle <= 9; iAngle++)
+			{
+				tOffset = tTotal = 0;
+
+				for(int j = 0, k = 0; j < nCells; j++)
+				{
+					if(pgc.angles[0][j].iAngle != 0
+					&& pgc.angles[0][j].iAngle != iAngle)
+						continue;
+
+					pgc.angles[iAngle].Add(pgc.angles[0][j]);
+
+					if(pgc.angles[iAngle][k].fDiscontinuity) tOffset = tTotal;
+
+					pgc.angles[iAngle][k].tTotal = tTotal;
+					pgc.angles[iAngle][k].tOffset = tOffset;
+
+					tTotal += pgc.angles[iAngle][k].tTime;
+
+					k++;
+				}
+			}
+		}
+	}
+
+    Log(LOG_INFO, _T("Parsing ifo OK"));
+
+	return(true);
+}
+
+bool CVobSubFileRipper::LoadVob(CString fn)
+{
+	Log(LOG_INFO, _T("Searching vobs..."));
+/*
+	CAtlList<CString> m_vobs;
+
+	fn = fn.Left(fn.ReverseFind('.')+1);
+	fn.TrimRight(_T(".0123456789"));
+	for(int i = 0; i < 100; i++)
+	{
+		CString vob;
+		vob.Format(_T("%s%d.vob"), fn, i);
+
+		CFileStatus status;
+		if(!(CFileGetStatus(vob, status) && status.m_size))
+		{
+			if(i > 0) break;
+			else continue;
+		}
+
+		if(status.m_size&0x7ff)
+		{
+			Log(LOG_ERROR, _T("Length of %s is not n*2048!"), vob);
+			m_vobs.RemoveAll();
+			break;
+		}
+
+		CString str = _T("Found ") + vob;
+
+		if(i == 0) 
+		{
+			str += _T(" (skipping, if not a menu vob rename it)");
+		}
+		else
+		{
+			m_vobs.AddTail(vob);
+		}
+
+		Log(LOG_INFO, str);
+	}
+
+	if(m_vobs.GetCount() <= 0)
+	{
+		Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
+		return(false);
+	}
+*/
+	CAtlList<CString> vobs;
+	if(!m_vob.Open(fn, vobs/*m_vobs*/))
+	{
+		Log(LOG_ERROR, _T("Cannot open vob sequence"));
+		return(false);
+	}
+
+	if(vobs.GetCount() <= 0)
+	{
+		Log(LOG_ERROR, _T("Nothing found! (%s*.vob)"), fn);
+		return(false);
+	}
+
+	POSITION pos = vobs.GetHeadPosition();
+	while(pos) Log(LOG_INFO, _T("Found ") + vobs.GetNext(pos));
+
+	if(m_vob.IsDVD())
+	{
+		Log(LOG_INFO, _T("DVD detected..."));
+
+		BYTE key[5];
+
+		if(m_vob.HasDiscKey(key))
+			Log(LOG_INFO, _T("Disc key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
+		else
+			Log(LOG_WARNING, _T("Couldn't get the disc key"));
+
+		if(m_vob.HasTitleKey(key))
+			Log(LOG_INFO, _T("Title key: %02x%02x%02x%02x%02x"), key[0], key[1], key[2], key[3], key[4]);
+		else
+			Log(LOG_WARNING, _T("Couldn't get the title key"));
+
+		BYTE buff[2048];
+
+		m_vob.Seek(0);
+		if(!m_vob.Read(buff))
+		{
+			Log(LOG_ERROR, _T("Can't read vob, please unlock it with a software player!"));
+			return(false);
+		}
+		m_vob.Seek(0);
+	}
+
+	return(true);
+}
+
+DWORD CVobSubFileRipper::ThreadProc()
+{	
+	SetThreadPriority(m_hThread, THREAD_PRIORITY_BELOW_NORMAL);
+
+    while(1)
+	{
+		DWORD cmd = GetRequest();
+
+		m_fThreadActive = true;
+
+		switch(cmd)
+		{
+		case CMD_EXIT:
+		    Reply(S_OK);
+			return 0;
+
+		case CMD_INDEX:
+			Reply(S_OK);
+			{
+			m_fIndexing = true;
+			bool fSucceeded = Create();
+			m_fIndexing = false;
+			Finished(fSucceeded);
+			}
+			break;
+
+		default:
+		    Reply(E_FAIL);
+			return -1;
+		}
+
+		m_fBreakThread = false;
+		m_fThreadActive = false;
+	}
+
+	return 1;
+}
+
+static int SubPosSortProc(const void* e1, const void* e2)
+{
+	return((int)(((CVobSubFile::SubPos*)e1)->start - ((CVobSubFile::SubPos*)e2)->start));
+}
+
+bool CVobSubFileRipper::Create()
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+
+	if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount())
+	{
+		Log(LOG_ERROR, _T("Invalid program chain number (%d)!"), m_rd.iSelPGC);
+		return(false);
+	}
+
+	PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
+
+	if(pgc.iSelAngle < 0 || pgc.iSelAngle > 9 || pgc.angles[pgc.iSelAngle].GetCount() == 0)
+	{
+		Log(LOG_ERROR, _T("Invalid angle number (%d)!"), pgc.iSelAngle);
+		return(false);
+	}
+
+	CAtlArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
+
+	if(m_rd.selids.GetCount() == 0 && !m_rd.fClosedCaption)
+	{
+		Log(LOG_ERROR, _T("No valid stream set to be extacted!"));
+		return(false);
+	}
+
+	if(m_rd.selvcs.GetCount() == 0)
+	{
+		Log(LOG_ERROR, _T("No valid vob/cell id set to be extacted!"));
+		return(false);
+	}
+
+	Log(LOG_INFO, _T("Indexing..."));
+
+	// initalize CVobSubFile
+	CVobSubFile::Close();
+	InitSettings();
+	m_title = m_outfn;
+	m_size = m_rd.vidsize;
+	TrimExtension(m_title);
+	memcpy(m_orgpal, pgc.pal, sizeof(m_orgpal));
+	m_sub.SetLength(0);
+
+	CCDecoder ccdec(m_title + _T(".cc.srt"), m_title + _T(".cc.raw"));
+
+	CVobDec vd;
+
+	__int64 SCR, PTS, tOffset = 0, tPrevOffset = 0, tTotal = 0, tStart = 0;
+	int vob = 0, cell = 0;
+	bool fDiscontinuity = false, fDiscontinuityFixApplied = false, fNavpackFound = false;
+
+	int PTSframeoffset = 0, minPTSframeoffset = 0;
+
+	if(m_rd.fResetTime)
+	{
+		for(int i = 0; i < angle.GetCount() && ((angle[i].vob<<16)|angle[i].cell) != m_rd.selvcs[0]; i++)
+			tStart += angle[i].tTime;
+
+		Log(LOG_INFO, _T("Counting timestamps from %I64dms (v%02dc%02d)"), 
+			tStart, m_rd.selvcs[0]>>16, m_rd.selvcs[0]&0xffff);
+	}
+
+	CAtlMap<DWORD, int> selvcmap;
+	selvcmap.RemoveAll();
+	for(int i = 0; i < m_rd.selvcs.GetCount(); i++)
+		selvcmap[m_rd.selvcs[i]] = 90000;
+
+	CAtlArray<vcchunk> chunks, foundchunks, loadedchunks;
+
+	if(m_vob.IsDVD())
+	{
+		Log(LOG_INFO, _T("Indexing mode: DVD"));
+
+		for(int i = 0; i < angle.GetCount(); i++)
+		{
+			DWORD vc = (angle[i].vob<<16)|angle[i].cell;
+			if(!selvcmap.Lookup(vc))
+				continue;
+
+			vcchunk c = {2048i64*angle[i].start, 2048i64*angle[i].end+2048, vc};
+			chunks.Add(c);
+
+			Log(LOG_INFO, _T("Adding: 0x%x - 0x%x (lba) for vob %d cell %d"), 
+				angle[i].start, angle[i].end, angle[i].vob, angle[i].cell);
+		}
+	}
+	else if(LoadChunks(loadedchunks))
+	{
+		Log(LOG_INFO, _T("Indexing mode: File"));
+
+		for(int i = 0; i < loadedchunks.GetCount(); i++)
+		{
+			DWORD vcid = loadedchunks[i].vc;
+			if(!selvcmap.Lookup(vcid))
+				continue;
+
+			chunks.Add(loadedchunks[i]);
+		}
+
+		Log(LOG_INFO, _T(".chunk file loaded"));
+	}
+	else
+	{
+		Log(LOG_INFO, _T("Indexing mode: File"));
+
+		chunks.RemoveAll();
+		vcchunk c = {0, 2048i64*m_vob.GetLength(), 0};
+		chunks.Add(c);
+	}
+
+	__int64 sizedone = 0, sizetotal = 0;
+	for(int i = 0; i < chunks.GetCount(); i++)
+		sizetotal += chunks[i].end - chunks[i].start;
+
+	for(int i = 0; !m_fBreakThread && i < chunks.GetCount(); i++)
+	{
+		__int64 curpos = chunks[i].start, endpos = chunks[i].end;
+
+		vcchunk curchunk = {curpos, curpos, chunks[i].vc};
+
+		for(m_vob.Seek((int)(curpos/2048)); !m_fBreakThread && curpos < endpos; curpos += 2048, sizedone += 2048)
+		{
+			if(!(curpos&0x7ffff))
+				Progress(1.0 * sizedone / sizetotal);
+
+			static BYTE buff[2048];
+
+			if(!m_vob.Read(buff))
+			{
+				Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
+				return(false);
+			}
+
+			curchunk.end = curpos;
+
+			if(buff[0x14] & 0x30)
+			{
+				if(!vd.m_fFoundKey)
+				{
+					Log(LOG_INFO, _T("Encrypted sector found, searching key..."));
+
+					__int64 savepos = curpos;
+
+					m_vob.Seek(0);
+					for(__int64 pos = 0; !m_fBreakThread && pos < endpos; pos += 2048)
+					{
+						if(!m_vob.Read(buff))
+						{
+							Log(LOG_ERROR, _T("Cannot read, either locked dvd or truncated/missing files!"));
+							return(false);
+						}
+
+						if(vd.FindKey(buff))
+							break;
+					}
+
+					if(m_fBreakThread)
+						break;
+
+					if(!vd.m_fFoundKey)
+					{
+						Log(LOG_ERROR, _T("Key not found, can't decrypt!"));
+						return(false);
+					}
+
+					Log(LOG_INFO, _T("Key found, continuing extraction..."));
+
+					m_vob.Seek((int)((curpos = savepos)/2048));
+					m_vob.Read(buff);
+				}
+
+				vd.Decrypt(buff);
+			}
+
+			if(*((DWORD*)&buff[0]) != 0xba010000)
+			{
+				Log(LOG_WARNING, _T("Bad sector header at block %08d!"), (int)(curpos/2048));
+
+				if(AfxMessageBox(_T("Bad packet header found, do you want to continue?"), MB_YESNO) == IDNO)
+				{
+					Log(LOG_ERROR, _T("Terminated!"));
+					return(false);
+				}
+			}
+			
+			SCR = (__int64(buff[0x04] & 0x38) >> 3) << 30
+				| __int64(buff[0x04] & 0x03) << 28
+				| __int64(buff[0x05]) << 20
+				| (__int64(buff[0x06] & 0xf8) >> 3) << 15
+				| __int64(buff[0x06] & 0x03) << 13
+				| __int64(buff[0x07]) << 5
+				| (__int64(buff[0x08] & 0xf8) >> 3) << 0;
+
+			bool hasPTS = false;
+				
+			if((*(DWORD*)&buff[0x0e] == 0xe0010000 || *(DWORD*)&buff[0x0e] == 0xbd010000)
+				&& buff[0x15] & 0x80)
+			{
+				PTS = (__int64)(buff[0x17] & 0x0e) << 29		// 32-30 (+marker)
+					| ((__int64)(buff[0x18]) << 22)				// 29-22
+					| ((__int64)(buff[0x19] & 0xfe) << 14)		// 21-15 (+marker)
+					| ((__int64)(buff[0x1a]) << 7)				// 14-07
+					| ((__int64)(buff[0x1b]) >> 1);				// 06-00 (+marker)
+				
+				hasPTS = true;
+			}
+
+			if(*((DWORD*)&buff[0x0e]) == 0xbb010000)
+			{
+				fNavpackFound = true;
+
+				if(vob == buff[0x420] && cell == buff[0x422])
+					continue;
+
+				vob = buff[0x420];
+				cell = buff[0x422];
+
+				tOffset = tTotal = 0;
+
+				for(int i = 0; i < angle.GetCount(); i++)
+				{
+					if(angle[i].vob == vob && angle[i].cell == cell)
+					{
+						tPrevOffset = tOffset;
+						tOffset = (__int64)angle[i].tOffset;
+						tTotal = (__int64)angle[i].tTotal;
+						fDiscontinuity = angle[i].fDiscontinuity;
+						fDiscontinuityFixApplied = false;
+						break;
+					}
+				}
+
+				if(curchunk.vc != ((vob<<16)|cell))
+				{
+					if(curchunk.vc != 0) foundchunks.Add(curchunk);
+					curchunk.start = curchunk.end = curpos;
+					curchunk.vc = (vob<<16)|cell;
+				}
+
+				CString str, str2;
+				str.Format(_T("v%02d c%02d lba%08d"), vob, cell, (int)(curpos/2048));
+				UINT vcid = (vob<<16)|cell;
+				if(!selvcmap.Lookup(vcid, minPTSframeoffset)) str2 = _T(", skipping");
+				else str2.Format(_T(", total=%I64dms, off=%I64dms, corr=%I64dms, discont.:%d"), 
+					tTotal, tOffset, -tStart, (int)fDiscontinuity);
+				Log(LOG_INFO, str + str2);
+			}
+
+			DWORD vcid = (vob<<16)|cell;
+			if(!selvcmap.Lookup(vcid, minPTSframeoffset))
+				continue;
+
+			if(hasPTS && fDiscontinuity && !fDiscontinuityFixApplied)
+			{
+				__int64 tDiff = tOffset - tPrevOffset;
+				if(tDiff > 0 && tDiff < (PTS/90+1000))
+				{
+					CString str;
+					str.Format(_T("False discontinuity detected, correcting time by %I64dms"), -tDiff);
+					Log(LOG_INFO, str);
+
+					tStart += tDiff;
+				}
+				fDiscontinuityFixApplied = true;
+			}
+
+			if(*(DWORD*)&buff[0x0e] == 0xe0010000)
+			{
+				if(fDiscontinuity)
+				{
+					if(PTS < minPTSframeoffset)
+					{
+						selvcmap[vcid] = PTSframeoffset = PTS;
+					}
+
+					fDiscontinuity = false;
+				}
+
+				if(m_rd.fClosedCaption)
+					ccdec.ExtractCC(buff, 2048, tOffset + ((PTS - PTSframeoffset) / 90) - tStart);
+			}
+			else if(*(DWORD*)&buff[0x0e] == 0xbd010000)
+			{
+				BYTE id = buff[0x17 + buff[0x16]], iLang = id&0x1f;
+
+				if((id & 0xe0) == 0x20 && m_rd.selids.Lookup(iLang))
+				{
+					if(hasPTS)
+					{
+						SubPos sb;
+						sb.filepos = m_sub.GetPosition();
+						sb.start = tOffset + ((PTS - PTSframeoffset) / 90) - tStart;
+						sb.vobid = (char)vob;
+						sb.cellid = (char)cell;
+						sb.celltimestamp = tTotal;
+						sb.fValid = true;
+						m_langs[iLang].subpos.Add(sb);
+					}
+
+					m_sub.Write(buff, 2048);
+				}
+			}
+		}
+
+		if(curchunk.vc != ((vob<<16)|cell))
+		{
+			if(curchunk.vc != 0) foundchunks.Add(curchunk);
+			curchunk.start = curchunk.end = curpos;
+			curchunk.vc = (vob<<16)|cell;
+		}
+	}
+
+	if(sizedone < sizetotal)
+	{
+		Log(LOG_ERROR, _T("Indexing terminated before reaching the end!"));
+		Progress(0);
+		return(false);
+	}
+
+	if(!fNavpackFound)
+	{
+		Log(LOG_ERROR, _T("Could not find any system header start code! (0x000001bb)"));
+		if(!m_vob.IsDVD()) Log(LOG_ERROR, _T("Make sure the ripper doesn't strip these packets."));
+		Progress(0);
+		return(false);
+	}
+
+	Log(LOG_INFO, _T("Indexing finished"));
+	Progress(1);
+
+	for(int i = 0; i < 32; i++)
+	{
+		if(m_iLang == -1 && m_langs[i].subpos.GetCount() > 0) m_iLang = i;
+		m_langs[i].id = pgc.ids[i];
+		m_langs[i].name = m_langs[i].alt = FindLangFromId(m_langs[i].id);
+
+		CAtlArray<SubPos>& sp = m_langs[i].subpos;
+		qsort(sp.GetData(), sp.GetCount(), sizeof(SubPos), SubPosSortProc);
+
+		if(m_rd.fForcedOnly)
+		{
+			Log(LOG_INFO, _T("Searching for forced subs..."));
+			Progress(0);
+
+			for(int j = 0, len = sp.GetCount(); j < len; j++)
+			{
+				Progress(1.0 * j / len);
+
+				sp[j].fValid = false;
+				int packetsize = 0, datasize = 0;
+				if(BYTE* buff = GetPacket(j, packetsize, datasize, i))
+				{
+					m_img.GetPacketInfo(buff, packetsize, datasize);
+					sp[j].fValid = m_img.fForced;
+					delete [] buff;
+				}
+			}
+
+			Progress(1);
+		}
+	}
+
+	Log(LOG_INFO, _T("Saving files..."));
+
+	if(m_iLang != -1)
+	{
+		if(!Save(m_title))
+		{
+			Log(LOG_ERROR, _T("Could not save output files!"));
+			return(false);
+		}
+	}
+
+	Log(LOG_INFO, _T("Subtitles saved"));
+
+	if(!m_vob.IsDVD() && loadedchunks.GetCount() == 0)
+	{
+		if(SaveChunks(foundchunks))
+		{
+			Log(LOG_INFO, _T(".chunk file saved"));
+		}
+	}
+
+	Log(LOG_INFO, _T("Done!"));
+
+	return(true);
+}
+
+static const DWORD s_version = 1;
+
+bool CVobSubFileRipper::LoadChunks(CAtlArray<vcchunk>& chunks)
+{
+	CFile f;
+
+	CString fn = m_infn;
+	TrimExtension(fn);
+	fn += _T(".chunks");
+
+	DWORD chksum = 0, chunklen, version;
+	__int64 voblen;
+
+	if(!f.Open(fn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+	f.Read(&version, sizeof(version));
+	if(version == 1)
+	{
+		f.Read(&chksum, sizeof(chksum));
+		f.Read(&voblen, sizeof(voblen));
+		f.Read(&chunklen, sizeof(chunklen));
+		chunks.SetCount(chunklen);
+		f.Read(chunks.GetData(), sizeof(vcchunk)*chunks.GetCount());
+	}
+	f.Close();
+
+	if(voblen != m_vob.GetLength())
+	{
+		chunks.RemoveAll();
+		return(false);
+	}
+
+	if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+	DWORD dw, chksum2 = 0;
+	while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum2 += dw;
+	f.Close();
+
+	if(chksum != chksum2)
+	{
+		chunks.RemoveAll();
+		return(false);
+	}
+
+	return(true);
+}
+
+bool CVobSubFileRipper::SaveChunks(CAtlArray<vcchunk>& chunks)
+{
+	CFile f;
+
+	CString fn = m_infn;
+	TrimExtension(fn);
+	fn += _T(".chunks");
+
+	DWORD chksum = 0, chunklen = chunks.GetCount();
+	__int64 voblen = m_vob.GetLength();
+
+	if(!f.Open(m_infn, CFile::modeRead|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+	DWORD dw;
+	while(f.Read(&dw, sizeof(dw)) == sizeof(dw)) chksum += dw;
+	f.Close();
+
+	if(!f.Open(fn, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary|CFile::shareDenyWrite))
+		return(false);
+	f.Write(&s_version, sizeof(s_version));
+	f.Write(&chksum, sizeof(chksum));
+	f.Write(&voblen, sizeof(voblen));
+	f.Write(&chunklen, sizeof(chunklen));
+	f.Write(chunks.GetData(), sizeof(vcchunk)*chunklen);
+	f.Close();
+
+	return(true);
+}
+
+// IVSFRipper
+
+STDMETHODIMP CVobSubFileRipper::SetCallBack(IVSFRipperCallback* pCallback)
+{
+	CAutoLock cAutoLock(&m_csCallback);
+	m_pCallback = pCallback;
+	return S_OK;
+}
+
+STDMETHODIMP CVobSubFileRipper::LoadParamFile(CString fn)
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+
+	m_rd.Reset();
+
+	CStdioFile f;
+	if(!f.Open(fn, CFile::modeRead|CFile::typeText))
+		return E_FAIL;
+
+	TCHAR langid[256];
+
+	enum {P_INPUT, P_OUTPUT, P_PGC, P_ANGLE, P_LANGS, P_OPTIONS};
+	int phase = P_INPUT;
+
+	CString line;
+	while(f.ReadString(line))
+	{
+		if(line.Trim().IsEmpty() || line[0] == '#') continue;
+
+		if(phase == P_INPUT)
+		{
+			if(S_OK != SetInput(line)) break;
+			phase = P_OUTPUT;
+		}
+		else if(phase == P_OUTPUT)
+		{
+			if(S_OK != SetOutput(line)) break;
+			phase = P_PGC;
+		}
+		else if(phase == P_PGC)
+		{
+			m_rd.iSelPGC = _tcstol(line, NULL, 10)-1;
+			if(m_rd.iSelPGC < 0 || m_rd.iSelPGC >= m_rd.pgcs.GetCount()) break;
+			phase = P_ANGLE;
+		}
+		else if(phase == 3)
+		{
+			PGC& pgc = m_rd.pgcs[m_rd.iSelPGC];
+
+			pgc.iSelAngle = _tcstol(line, NULL, 10);
+			if(pgc.iSelAngle < 0 || pgc.iSelAngle > max(1, pgc.nAngles) || pgc.iSelAngle > 9) break;
+
+			CAtlArray<vc_t>& angle = pgc.angles[pgc.iSelAngle];
+
+			if(line.Find('v') >= 0)
+			{
+				int vob = 0, cell = 0;
+
+				line += ' ';
+
+				TCHAR* s = (LPTSTR)(LPCTSTR)line;
+				TCHAR* e = s + line.GetLength();
+				while(s < e)
+				{
+					if(*s == 'v' || s == e-1)
+					{
+						s++;
+						if(vob != 0 && cell == 0)
+						{
+							for(int i = 0; i < angle.GetCount(); i++)
+							{
+								if(angle[i].vob == vob)
+									m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
+							}
+						}
+
+						vob = _tcstol(s, &s, 10);
+						cell = 0;
+					}
+					else if(*s == 'c' && vob > 0)
+					{
+						s++;
+						cell = _tcstol(s, &s, 10);
+
+						for(int i = 0; i < angle.GetCount(); i++)
+						{
+							if(angle[i].vob == vob && angle[i].cell == cell)
+							{
+								m_rd.selvcs.Add((vob<<16)|cell);
+								break;
+							}
+						}
+					}
+					else
+					{
+						s++;
+					}
+				}
+			}
+			else
+			{
+				for(int i = 0; i < angle.GetCount(); i++)
+					m_rd.selvcs.Add((angle[i].vob<<16)|angle[i].cell);
+			}
+
+			phase = P_LANGS;
+		}
+		else if(phase == 4)
+		{
+			if(!line.CompareNoCase(_T("ALL")))
+			{
+				for(int i = 0; i < 32; i++) m_rd.selids[i] = true;
+				m_rd.fClosedCaption = true;
+				phase = P_OPTIONS;
+			}
+			else
+			{
+				line += ' ';
+
+				while(line.GetLength() > 0)
+				{
+					int n = line.Find(_T(" "));
+
+					CString lang = line.Left(n);
+
+					line = line.Mid(n);
+					line.TrimLeft();
+
+					n = 0;
+
+					int langnum;
+
+					if(_istdigit(lang[0])) 
+					{
+						n = _stscanf(lang, _T("%d"), &langnum);
+						if(n != 1) break;
+
+						m_rd.selids[langnum] = true;
+					}
+					else if(_istalpha(lang[0])) 
+					{
+						n = _stscanf(lang, _T("%s"), langid);
+						if(n != 1) break;
+
+						int id = (langid[0] << 8) + langid[1];
+
+						if(id == 'cc')
+						{
+							m_rd.fClosedCaption = true;
+						}
+						else
+						{
+							m_rd.selids[id] = true;
+						}
+					}
+					else break;
+
+					if(n != 1) break;
+				}
+
+				if((m_rd.selids.GetCount() > 0 || m_rd.fClosedCaption) && line.IsEmpty())
+					phase = P_OPTIONS;
+			}
+		}
+		else if(phase == 5 && !line.CompareNoCase(_T("CLOSE")))
+			m_rd.fClose = true;
+		else if(phase == 5 && !line.CompareNoCase(_T("BEEP")))
+			m_rd.fBeep = true;
+		else if(phase == 5 && !line.CompareNoCase(_T("RESETTIME")))
+			m_rd.fResetTime = true;
+		else if(phase == 5 && !line.CompareNoCase(_T("FORCEDONLY")))
+			m_rd.fForcedOnly = true;
+		else if(phase == 5 && !line.CompareNoCase(_T("CLOSEIGNOREERRORS")))
+			m_rd.fCloseIgnoreError = true;
+		
+	}
+
+	m_rd.fAuto = true;
+
+	return phase == P_OPTIONS ? S_OK : E_FAIL;
+}
+
+STDMETHODIMP CVobSubFileRipper::SetInput(CString infn)
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+
+	m_rd.Reset();
+
+	if(!LoadIfo(infn) || !LoadVob(infn))
+		return E_INVALIDARG;
+
+	m_infn = infn;
+
+	return S_OK;
+}
+
+STDMETHODIMP CVobSubFileRipper::SetOutput(CString outfn)
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+	m_outfn = outfn;
+	return S_OK;
+}
+
+STDMETHODIMP CVobSubFileRipper::GetRipperData(VSFRipperData& rd)
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+	rd.Copy(m_rd);
+	return S_OK;
+}
+
+STDMETHODIMP CVobSubFileRipper::UpdateRipperData(VSFRipperData& rd)
+{
+	CAutoLock cAutoLock(&m_csAccessLock);
+	m_rd.Copy(rd);
+	return S_OK;
+}
+
+STDMETHODIMP CVobSubFileRipper::Index()
+{
+	if(m_fIndexing) return E_FAIL;
+	CAMThread::CallWorker(CMD_INDEX);
+	return S_OK;
+}
+
+
+STDMETHODIMP CVobSubFileRipper::IsIndexing()
+{
+	return m_fIndexing ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP CVobSubFileRipper::Abort(bool fSavePartial)
+{
+	m_fBreakThread = true;
+	return S_OK;
+}
+
+//
+
+void VSFRipperData::Reset()
+{
+	vidsize.SetSize(0,0);
+	memset(&vidinfo, 0, sizeof(vidinfo));
+	pgcs.RemoveAll();
+	iSelPGC = -1;
+	fResetTime = fClosedCaption = true;
+	fForcedOnly = false;
+	fClose = fBeep = fAuto = false;
+	fCloseIgnoreError = false;
+
+	selvcs.RemoveAll();
+	selids.RemoveAll();
+}
+
+void VSFRipperData::Copy(VSFRipperData& rd)
+{
+	Reset();
+
+	vidsize = rd.vidsize;
+	vidinfo = rd.vidinfo;
+	if(int len = rd.pgcs.GetCount())
+	{
+		pgcs.SetCount(len);
+		for(int i = 0; i < len; i++)
+		{
+			PGC& src = rd.pgcs[i];
+			PGC& dst = pgcs[i];
+			dst.nAngles = src.nAngles;
+			for(int i = 0; i < countof(dst.angles); i++)
+				dst.angles[i].Copy(src.angles[i]);
+			dst.iSelAngle = src.iSelAngle;
+			memcpy(dst.pal, src.pal, sizeof(src.pal));
+			memcpy(dst.ids, src.ids, sizeof(src.ids));
+		}
+	}
+	iSelPGC = rd.iSelPGC;
+	fResetTime = rd.fResetTime;
+	fClosedCaption = rd.fClosedCaption;
+	fForcedOnly = rd.fForcedOnly;
+	fClose = rd.fClose;
+	fBeep = rd.fBeep;
+	fAuto = rd.fAuto;
+	fCloseIgnoreError = rd.fCloseIgnoreError;
+	selvcs.Copy(rd.selvcs);
+	POSITION pos = rd.selids.GetStartPosition();
+	while(pos)
+	{
+		BYTE key;
+		bool val;
+		rd.selids.GetNextAssoc(pos, key, val);
+		selids[key] = val;
+	}
+}
+
diff --git a/vsfilter/subtitles/VobSubFileRipper.h b/vsfilter/subtitles/VobSubFileRipper.h
new file mode 100644
index 0000000000000000000000000000000000000000..023729aad90e2de94af39b1306511b6740875056
--- /dev/null
+++ b/vsfilter/subtitles/VobSubFileRipper.h
@@ -0,0 +1,193 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+#include "..\include\decss\VobFile.h"
+#include "VobSubFile.h"
+
+#pragma pack(push)
+#pragma pack(1)
+
+typedef struct
+{
+	WORD perm_displ       : 2;
+	WORD ratio            : 2;
+	WORD system           : 2;
+	WORD compression      : 2;
+	WORD mode             : 1;
+	WORD letterboxed      : 1;
+	WORD source_res       : 2;
+	WORD cbrvbr           : 2;
+	WORD line21_2         : 1;
+	WORD line21_1         : 1;
+} vidinfo;
+
+typedef struct 
+{
+	BYTE vob, cell; 
+	DWORD tTime, tOffset, tTotal; 
+	DWORD start, end;
+	int iAngle; 
+	bool fDiscontinuity;
+} vc_t;
+
+typedef struct 
+{
+	int nAngles;
+	CAtlArray<vc_t> angles[10];
+	int iSelAngle;
+	RGBQUAD pal[16];
+	WORD ids[32];
+} PGC;
+
+typedef struct VSFRipperData_t
+{
+	CSize vidsize;
+	vidinfo vidinfo;
+	CAtlArray<PGC> pgcs;
+	int iSelPGC;
+	bool fResetTime, fClosedCaption, fForcedOnly;
+
+	bool fClose, fBeep, fAuto; // only used by the UI externally, but may be set through the parameter file
+	bool fCloseIgnoreError;
+
+	CAtlArray<UINT> selvcs;
+	CAtlMap<BYTE, bool> selids;
+
+	void Reset();
+	void Copy(struct VSFRipperData_t& rd);
+
+} VSFRipperData;
+
+typedef struct {__int64 start, end; DWORD vc;} vcchunk;
+
+#pragma pack(pop)
+
+// note: these interfaces only meant to be used internally with static linking
+
+//
+// IVSFRipperCallback
+//
+
+[uuid("9E2EBB5C-AD7C-452f-A48B-38685716AC46")]
+interface IVSFRipperCallback : public IUnknown
+{
+	STDMETHOD (OnMessage) (LPCTSTR msg) PURE;
+	STDMETHOD (OnProgress) (double progress /*0->1*/) PURE;
+	STDMETHOD (OnFinished) (bool fSucceeded) PURE;
+};
+
+// IVSFRipperCallbackImpl
+
+#ifndef QI
+#define QI(i) (riid == __uuidof(i)) ? GetInterface((i*)this, ppv) :
+#endif
+
+class IVSFRipperCallbackImpl : public CUnknown, public IVSFRipperCallback
+{
+protected:
+	DECLARE_IUNKNOWN
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv)
+	{
+		return 
+			QI(IVSFRipperCallback)
+			__super::NonDelegatingQueryInterface(riid, ppv);
+	}
+
+	// IVSFRipperCallback
+	STDMETHODIMP OnMessage(LPCTSTR msg) {return S_FALSE;}
+	STDMETHODIMP OnProgress(double progress /*0->1*/) {return S_FALSE;}
+	STDMETHODIMP OnFinished(bool fSucceeded) {return S_FALSE;}
+
+public:
+	IVSFRipperCallbackImpl() : CUnknown(NAME("IVSFRipperCallbackImpl"), NULL) {}
+};
+
+//
+// IVSFRipper
+//
+
+[uuid("69F935BB-B8D0-43f5-AA2E-BBD0851CC9A6")]
+interface IVSFRipper : public IUnknown
+{
+	STDMETHOD (SetCallBack) (IVSFRipperCallback* pCallback) PURE;
+	STDMETHOD (LoadParamFile) (CString fn) PURE;
+	STDMETHOD (SetInput) (CString infn) PURE;
+	STDMETHOD (SetOutput) (CString outfn) PURE;
+	STDMETHOD (GetRipperData) (VSFRipperData& rd) PURE;
+	STDMETHOD (UpdateRipperData) (VSFRipperData& rd) PURE;
+	STDMETHOD (Index) () PURE;
+	STDMETHOD (IsIndexing) () PURE;
+	STDMETHOD (Abort) (bool fSavePartial) PURE;
+};
+
+class CVobSubFileRipper : public CVobSubFile, protected CAMThread, public IVSFRipper
+{
+private:
+	bool m_fThreadActive, m_fBreakThread, m_fIndexing;
+	enum {CMD_EXIT, CMD_INDEX};
+	DWORD ThreadProc();
+	bool Create();
+
+	//
+
+	typedef enum {LOG_INFO, LOG_WARNING, LOG_ERROR} log_t;
+	void Log(log_t type, LPCTSTR lpszFormat, ...);
+	void Progress(double progress);
+	void Finished(bool fSucceeded);
+
+	//
+
+	CCritSec m_csAccessLock;
+	CString m_infn, m_outfn;
+	CVobFile m_vob;
+	VSFRipperData m_rd;
+
+	bool LoadIfo(CString fn);
+	bool LoadVob(CString fn);
+	bool LoadChunks(CAtlArray<vcchunk>& chunks);
+	bool SaveChunks(CAtlArray<vcchunk>& chunks);
+
+	//
+
+	CCritSec m_csCallback;
+	CComPtr<IVSFRipperCallback> m_pCallback;
+
+public:
+    CVobSubFileRipper();
+    virtual ~CVobSubFileRipper();
+
+	DECLARE_IUNKNOWN
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void** ppv);
+
+	// IVSFRipper
+	STDMETHODIMP SetCallBack(IVSFRipperCallback* pCallback);
+	STDMETHODIMP LoadParamFile(CString fn);
+	STDMETHODIMP SetInput(CString infn);
+	STDMETHODIMP SetOutput(CString outfn);
+	STDMETHODIMP GetRipperData(VSFRipperData& rd);
+	STDMETHODIMP UpdateRipperData(VSFRipperData& rd);
+	STDMETHODIMP Index();
+	STDMETHODIMP IsIndexing();
+	STDMETHODIMP Abort(bool fSavePartial);
+};
diff --git a/vsfilter/subtitles/VobSubImage.cpp b/vsfilter/subtitles/VobSubImage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8485a77147b42c37ad22667ec39bdd583225a873
--- /dev/null
+++ b/vsfilter/subtitles/VobSubImage.cpp
@@ -0,0 +1,1188 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "stdafx.h"
+#include "VobSubImage.h"
+
+CVobSubImage::CVobSubImage()
+{
+	iLang = iIdx = -1;
+	fForced = false;
+	start = delay = 0;
+	rect = CRect(0,0,0,0);
+	lpPixels = lpTemp1 = lpTemp2 = NULL;
+	org = CSize(0,0);
+}
+
+CVobSubImage::~CVobSubImage()
+{
+	Free();
+}
+
+bool CVobSubImage::Alloc(int w, int h)
+{
+	// if there is nothing to crop TrimSubImage might even add a 1 pixel
+	// wide border around the text, that's why we need a bit more memory
+	// to be allocated.
+
+	if(lpTemp1 == NULL || w*h > org.cx*org.cy)
+	{
+		Free();
+
+		lpTemp1 = new RGBQUAD[w*h];
+		if(!lpTemp1) return(false);
+
+		lpTemp2 = new RGBQUAD[(w+2)*(h+2)];
+		if(!lpTemp2) {delete [] lpTemp1; lpTemp1 = NULL; return(false);}
+
+		org.cx = w; 
+		org.cy = h;
+	}
+
+	lpPixels = lpTemp1;
+
+	return(true);
+}
+
+void CVobSubImage::Free()
+{
+	if(lpTemp1) delete [] lpTemp1;
+	lpTemp1 = NULL;
+
+	if(lpTemp2) delete [] lpTemp2;
+	lpTemp2 = NULL;
+
+	lpPixels = NULL;
+}
+
+bool CVobSubImage::Decode(BYTE* lpData, int packetsize, int datasize,
+						  bool fCustomPal, 
+						  int tridx, 
+						  RGBQUAD* orgpal /*[16]*/, RGBQUAD* cuspal /*[4]*/,
+						  bool fTrim)
+{
+	GetPacketInfo(lpData, packetsize, datasize);
+
+	if(!Alloc(rect.Width(), rect.Height())) return(false);
+
+	lpPixels = lpTemp1;
+
+	nPlane = 0;
+	fAligned = 1;
+
+	this->fCustomPal = fCustomPal;
+	this->orgpal = orgpal;
+	this->tridx = tridx;
+	this->cuspal = cuspal;
+
+	CPoint p(rect.left, rect.top);
+
+	int end0 = nOffset[1];
+	int end1 = datasize;
+
+	while((nPlane == 0 && nOffset[0] < end0) || (nPlane == 1 && nOffset[1] < end1))
+	{
+		DWORD code;
+
+		if((code = GetNibble(lpData)) >= 0x4
+		|| (code = (code << 4) | GetNibble(lpData)) >= 0x10
+		|| (code = (code << 4) | GetNibble(lpData)) >= 0x40
+		|| (code = (code << 4) | GetNibble(lpData)) >= 0x100)
+		{
+			DrawPixels(p, code >> 2, code & 3);
+			if((p.x += code >> 2) < rect.right) continue;
+		}
+
+		DrawPixels(p, rect.right - p.x, code & 3);
+
+		if(!fAligned) GetNibble(lpData); // align to byte
+
+		p.x = rect.left;
+		p.y++;
+		nPlane = 1 - nPlane;
+	}
+
+	rect.bottom = min(p.y, rect.bottom);
+
+	if(fTrim) TrimSubImage();
+
+	return(true);
+}
+
+void CVobSubImage::GetPacketInfo(BYTE* lpData, int packetsize, int datasize)
+{
+//	delay = 0;
+
+	int i, nextctrlblk = datasize;
+	WORD pal = 0, tr = 0;
+
+	do
+	{
+		i = nextctrlblk;
+
+		int t = (lpData[i] << 8) | lpData[i+1]; i += 2;
+		nextctrlblk = (lpData[i] << 8) | lpData[i+1]; i += 2;
+
+		if(nextctrlblk > packetsize || nextctrlblk < datasize)
+		{
+			ASSERT(0);
+			return;
+		}
+
+		bool fBreak = false;
+
+		while(!fBreak)
+		{
+			int len = 0;
+
+			switch(lpData[i])
+			{
+				case 0x00: len = 0; break;
+				case 0x01: len = 0; break;
+				case 0x02: len = 0; break;
+				case 0x03: len = 2; break;
+				case 0x04: len = 2; break;
+				case 0x05: len = 6; break;
+				case 0x06: len = 4; break;
+				default: len = 0; break;
+			}
+
+			if(i+len >= packetsize)
+			{
+				TRACE(_T("Warning: Wrong subpicture parameter block ending\n"));
+				break;
+			}
+
+			switch(lpData[i++])
+			{
+				case 0x00: // forced start displaying
+					fForced = true;
+					break;
+				case 0x01: // start displaying
+					fForced = false;
+					break;
+				case 0x02: // stop displaying
+					delay = 1024 * t / 90;
+					break;
+				case 0x03:
+					pal = (lpData[i] << 8) | lpData[i+1]; i += 2;
+					break;
+				case 0x04:
+					tr = (lpData[i] << 8) | lpData[i+1]; i += 2;
+//tr &= 0x00f0;
+					break;
+				case 0x05:
+					rect = CRect((lpData[i] << 4) + (lpData[i+1] >> 4), 
+						(lpData[i+3] << 4) + (lpData[i+4] >> 4), 
+						((lpData[i+1] & 0x0f) << 8) + lpData[i+2] + 1, 
+						((lpData[i+4] & 0x0f) << 8) + lpData[i+5] + 1);
+					i += 6;
+					break;
+				case 0x06:
+					nOffset[0] = (lpData[i] << 8) + lpData[i+1]; i += 2;
+					nOffset[1] = (lpData[i] << 8) + lpData[i+1]; i += 2;
+					break;
+				case 0xff: // end of ctrlblk
+					fBreak = true;
+					continue;
+				default: // skip this ctrlblk
+					fBreak = true;
+					break;
+			}
+		}
+	}
+	while(i <= nextctrlblk && i < packetsize);
+
+	for(i = 0; i < 4; i++) 
+	{
+		this->pal[i].pal = (pal >> (i << 2)) & 0xf;
+		this->pal[i].tr = (tr >> (i << 2)) & 0xf;
+	}
+}
+
+BYTE CVobSubImage::GetNibble(BYTE* lpData)
+{
+	WORD& off = nOffset[nPlane];
+	BYTE ret = (lpData[off] >> (fAligned << 2)) & 0x0f;
+	fAligned = !fAligned;
+	off += fAligned;
+	return(ret);
+}
+
+void CVobSubImage::DrawPixels(CPoint p, int length, int colorid)
+{
+	if(length <= 0
+	|| p.x + length < rect.left
+	|| p.x >= rect.right
+	|| p.y < rect.top
+	|| p.y >= rect.bottom) 
+	{
+		return;
+	}
+
+	if(p.x < rect.left) p.x = rect.left;
+	if(p.x + length >= rect.right) length = rect.right - p.x;
+
+	RGBQUAD* ptr = &lpPixels[rect.Width() * (p.y - rect.top) + (p.x - rect.left)];
+
+	RGBQUAD c;
+
+	if(!fCustomPal) 
+	{
+		c = orgpal[pal[colorid].pal];
+		c.rgbReserved = (pal[colorid].tr<<4)|pal[colorid].tr;
+	}
+	else
+	{
+		c = cuspal[colorid];
+	}
+
+	while(length-- > 0) *ptr++ = c;
+}
+
+void CVobSubImage::TrimSubImage()
+{
+	CRect r;
+	r.left = rect.Width();
+	r.top = rect.Height();
+	r.right = 0;
+	r.bottom = 0;
+
+	RGBQUAD* ptr = lpTemp1;
+
+	for(int j = 0, y = rect.Height(); j < y; j++)
+	{
+		for(int i = 0, x = rect.Width(); i < x; i++, ptr++)
+		{
+			if(ptr->rgbReserved)
+			{
+				if(r.top > j) r.top = j;
+				if(r.bottom < j) r.bottom = j;
+				if(r.left > i) r.left = i; 
+				if(r.right < i) r.right = i; 
+			}
+		}
+	}
+
+	if(r.left > r.right || r.top > r.bottom) return;
+
+	r += CRect(0, 0, 1, 1);
+
+	r &= CRect(CPoint(0,0), rect.Size());
+
+	int w = r.Width(), h = r.Height();
+
+	DWORD offset = r.top*rect.Width() + r.left;
+
+	r += CRect(1, 1, 1, 1);
+
+	DWORD* src = (DWORD*)&lpTemp1[offset];
+	DWORD* dst = (DWORD*)&lpTemp2[1 + w + 1];
+
+	memset(lpTemp2, 0, (1 + w + 1)*sizeof(RGBQUAD));
+
+	for(int height = h; height; height--, src += rect.Width())
+	{
+		*dst++ = 0;
+		memcpy(dst, src, w*sizeof(RGBQUAD)); dst += w; 
+		*dst++ = 0;
+	}
+
+	memset(dst, 0, (1 + w + 1)*sizeof(RGBQUAD));
+
+	lpPixels = lpTemp2;
+
+	rect = r + rect.TopLeft();
+}
+
+////////////////////////////////
+
+#include "RTS.h"
+#include <math.h>
+
+#define GP(xx, yy) (((xx) < 0 || (yy) < 0 || (xx) >= w || (yy) >= h) ? 0 : p[(yy)*w+(xx)])
+
+CAutoPtrList<COutline>* CVobSubImage::GetOutlineList(CPoint& topleft)
+{
+	int w = rect.Width(), h = rect.Height(), len = w*h;
+	if(len <= 0) return NULL;
+
+	CAutoVectorPtr<BYTE> p;
+	if(!p.Allocate(len)) return NULL;
+
+	CAutoPtrList<COutline>* ol = new CAutoPtrList<COutline>();
+	if(!ol) return NULL;
+
+	BYTE* cp = p;
+	RGBQUAD* rgbp = (RGBQUAD*)lpPixels;
+
+	for(int i = 0; i < len; i++, cp++, rgbp++)
+		*cp = !!rgbp->rgbReserved;
+
+	enum {UP, RIGHT, DOWN, LEFT};
+
+	topleft.x = topleft.y = INT_MAX;
+
+	while(1)
+	{
+		cp = p;
+
+		int x, y; 
+
+		for(y = 0; y < h; y++)
+		{
+			for(x = 0; x < w-1; x++, cp++)
+			{
+				if(cp[0] == 0 && cp[1] == 1) break;
+			}
+
+			if(x < w-1) break;
+
+			cp++;
+		}
+
+		if(y == h) break;
+
+		int prevdir, dir = UP;
+
+		int ox = x, oy = y, odir = dir;
+
+		CAutoPtr<COutline> o(new COutline);
+		if(!o) break;
+
+		do
+		{
+			CPoint pp;
+			BYTE fl, fr, br;
+
+			prevdir = dir;
+
+			switch(prevdir)
+			{
+			case UP:
+				pp = CPoint(x+1, y);
+				fl = GP(x, y-1);
+				fr = GP(x+1, y-1);
+				br = GP(x+1, y);
+				break;
+			case RIGHT:
+				pp = CPoint(x+1, y+1);
+				fl = GP(x+1, y);
+				fr = GP(x+1, y+1);
+				br = GP(x, y+1);
+				break;
+			case DOWN:
+				pp = CPoint(x, y+1);
+				fl = GP(x, y+1);
+				fr = GP(x-1, y+1);
+				br = GP(x-1, y);
+				break;
+			case LEFT:
+				pp = CPoint(x, y);
+				fl = GP(x-1, y);
+				fr = GP(x-1, y-1);
+				br = GP(x, y-1);
+				break;
+			}
+
+			// turning left if:
+			// o . | o .
+			// ^ o | < o
+			// turning right if:
+			// x x | x >
+			// ^ o | x o
+			//
+			// o set, x empty, . can be anything
+
+			if(fl==1) dir = (dir-1+4)&3;
+			else if(fl!=1 && fr!=1 && br==1) dir = (dir+1)&3;
+			else if(p[y*w+x]&16) {ASSERT(0); break;} // we are going around in one place (this must not happen if the starting conditions were correct)
+
+			p[y*w+x] = (p[y*w+x]<<1) | 2; // increase turn count (== log2(highordbit(*p)))
+
+			switch(dir)
+			{
+			case UP:
+				if(prevdir == LEFT) {x--; y--;}
+				if(prevdir == UP) y--;
+				break;
+			case RIGHT:
+				if(prevdir == UP) {x++; y--;}
+				if(prevdir == RIGHT) x++;
+				break;
+			case DOWN:
+				if(prevdir == RIGHT) {x++; y++;}
+				if(prevdir == DOWN) y++;
+				break;
+			case LEFT:
+				if(prevdir == DOWN) {x--; y++;}
+				if(prevdir == LEFT) x--;
+				break;
+			}
+
+			int d = dir - prevdir;
+			o->Add(pp, d == 3 ? -1 : d == -3 ? 1 : d);
+
+			if(topleft.x > pp.x) topleft.x = pp.x;
+			if(topleft.y > pp.y) topleft.y = pp.y;
+		}
+		while(!(x == ox && y == oy && dir == odir));
+
+		if(o->pa.GetCount() > 0 && (x == ox && y == oy && dir == odir)) 
+		{
+			ol->AddTail(o);
+		}
+		else
+		{
+			ASSERT(0);
+		}
+	}
+
+	return(ol);
+}
+
+static bool FitLine(COutline& o, int& start, int& end)
+{
+	int len = o.pa.GetCount();
+	if(len < 7) return(false); // small segments should be handled with beziers...
+
+	for(start = 0; start < len && !o.da[start]; start++);
+	for(end = len-1; end > start && !o.da[end]; end--);
+
+	if(end-start < 8 || end-start < (len-end)+(start-0)) return(false);
+
+	CUIntArray la, ra;
+
+	int i, j, k;
+
+	for(i = start+1, j = end, k = start; i <= j; i++)
+	{
+		if(!o.da[i]) continue;
+		if(o.da[i] == o.da[k]) return(false);
+		if(o.da[i] == -1) la.Add(i-k);
+		else ra.Add(i-k);
+		k = i;
+	}
+
+	bool fl = true, fr = true;
+
+	// these tests are completly heuristic and might be redundant a bit...
+
+	for(i = 0, j = la.GetSize(); i < j && fl; i++) {if(la[i] != 1) fl = false;} 
+	for(i = 0, j = ra.GetSize(); i < j && fr; i++) {if(ra[i] != 1) fr = false;}
+
+	if(!fl && !fr) return(false); // can't be a line if there are bigger steps than one in both directions (lines are usually drawn by stepping one either horizontally or vertically)
+	if(fl && fr && 1.0*(end-start)/((len-end)*2+(start-0)*2) > 0.4) return(false); // if this section is relatively too small it may only be a rounded corner
+	if(!fl && la.GetSize() > 0 && la.GetSize() <= 4 && (la[0] == 1 && la[la.GetSize()-1] == 1)) return(false); // one step at both ends, doesn't sound good for a line (may be it was skewed, so only eliminate smaller sections where beziers going to look just as good)
+	if(!fr && ra.GetSize() > 0 && ra.GetSize() <= 4 && (ra[0] == 1 && ra[ra.GetSize()-1] == 1)) return(false); // -''-
+
+	CUIntArray& a = !fl ? la : ra;
+
+	len = a.GetSize();
+
+	int sum = 0;
+
+	for(i = 0, j = INT_MAX, k = 0; i < len; i++)
+	{
+		if(j > a[i]) j = a[i];
+		if(k < a[i]) k = a[i];
+		sum += a[i];
+	}
+
+	if(k - j > 2 && 1.0*sum/len < 2) return(false);
+	if(k - j > 2 && 1.0*sum/len >= 2 && len < 4) return(false);
+
+	if((la.GetSize()/2+ra.GetSize()/2)/2 <= 2)
+	{
+		if((k+j)/2 < 2 && k*j!=1) return(false);
+	}
+
+	double err = 0;
+
+	CPoint sp = o.pa[start], ep = o.pa[end];
+
+	double minerr = 0, maxerr = 0;
+	
+	double vx = ep.x - sp.x, vy = ep.y - sp.y, l = sqrt(vx*vx+vy*vy);
+	vx /= l; vy /= l;
+
+	for(i = start+1, j = end-1; i <= j; i++)
+	{
+		CPoint p = o.pa[i], dp = p - sp;
+		double t = vx*dp.x+vy*dp.y, dx = vx*t + sp.x - p.x, dy = vy*t + sp.y - p.y;
+		t = dx*dx+dy*dy;
+		err += t;
+		t = sqrt(t);
+		if(vy*dx-dy*vx < 0) {if(minerr > -t) minerr = -t;}
+		else {if(maxerr < t) maxerr = t;}
+	}
+
+	return((maxerr-minerr)/l < 0.1  || err/l < 1.5 || (fabs(maxerr) < 8 && fabs(minerr) < 8));
+}
+
+static int CalcPossibleCurveDegree(COutline& o)
+{
+	int len2 = o.da.GetCount();
+
+	CUIntArray la;
+
+	for(int i = 0, j = 0; j < len2; j++)
+	{
+		if(j == len2-1 || o.da[j])
+		{
+			la.Add(j-i);
+			i = j;
+		}
+	}
+
+	int len = la.GetCount();
+
+	int ret = 0;
+
+	// check if we can find a reason to add a penalty degree, or two :P
+	// it is mainly about looking for distant corners
+	{
+		int penalty = 0;
+
+		int ma[2] = {0, 0};
+		for(int i = 0; i < len; i++) ma[i&1] += la[i];
+
+		int ca[2] = {ma[0], ma[1]};
+		for(int i = 0; i < len; i++) 
+		{
+			ca[i&1] -= la[i];
+
+			double c1 = 1.0*ca[0]/ma[0], c2 = 1.0*ca[1]/ma[1], c3 = 1.0*la[i]/ma[i&1];
+
+			if(len2 > 16 && (fabs(c1-c2) > 0.7 || (c3 > 0.6 && la[i] > 5)))
+				{penalty = 2; break;}
+
+			if(fabs(c1-c2) > 0.6 || (c3 > 0.4 && la[i] > 5))
+				{penalty = 1;}
+		}
+
+		ret += penalty;
+	}
+
+	la[0] <<= 1;
+	la[len-1] <<= 1;
+
+	for(int i = 0; i < len; i+=2)
+	{
+		if(la[i] > 1) {ret++; i--;} // prependicular to the last chosen section and bigger then 1 -> add a degree and continue with the other dir
+	}
+
+	return(ret);
+}
+
+inline double vectlen(CPoint p)
+{
+	return(sqrt((double)(p.x*p.x+p.y*p.y)));
+}
+
+inline double vectlen(CPoint p1, CPoint p2)
+{
+	return(vectlen(p2 - p1));
+}
+
+static bool MinMaxCosfi(COutline& o, double& mincf, double& maxcf) // not really cosfi, it is weighted by the distance from the segment endpoints, and since it would be always between -1 and 0, the applied sign marks side 
+{
+	CAtlArray<CPoint>& pa = o.pa;
+
+	int len = (int)pa.GetCount();
+	if(len < 6) return(false);
+
+	mincf = 1;
+	maxcf = -1;
+
+	CPoint p = pa[len-1] - pa[0];
+	double l = vectlen(p);
+
+	for(int i = 2; i < len-2; i++) // skip the endpoints, they aren't accurate
+	{
+		CPoint p1 = pa[0] - pa[i], p2 = pa[len-1] - pa[i];
+		double l1 = vectlen(p1), l2 = vectlen(p2);
+		int sign = p1.x*p.y-p1.y*p.x >= 0 ? 1 : -1;
+
+		double c = (1.0*len/2 - fabs(i - 1.0*len/2)) / len * 2; // c: 0 -> 1 -> 0
+
+		double cosfi = (1+(p1.x*p2.x+p1.y*p2.y)/(l1*l2)) * sign * c;
+		if(mincf > cosfi) mincf = cosfi;
+		if(maxcf < cosfi) maxcf = cosfi;
+	}
+
+	return(true);
+}
+
+static bool FitBezierVH(COutline& o, CPoint& p1, CPoint& p2)
+{
+	int i;
+
+	CAtlArray<CPoint>& pa = o.pa;
+
+	int len = (int)pa.GetCount();
+
+	if(len <= 1)
+	{
+		return(false);
+	}
+	else if(len == 2)
+	{
+		CPoint mid = pa[0]+pa[1];
+		mid.x >>= 1;
+		mid.y >>= 1;
+		p1 = p2 = mid;
+		return(true);
+	}
+
+	CPoint dir1 = pa[1] - pa[0], dir2 = pa[len-2] - pa[len-1];
+	if((dir1.x&&dir1.y)||(dir2.x&&dir2.y)) 
+		return(false); // we are only fitting beziers with hor./ver. endings
+
+	if(CalcPossibleCurveDegree(o) > 3) 
+		return(false);
+	
+	double mincf, maxcf;
+	if(MinMaxCosfi(o, mincf, maxcf))
+	{
+		if(maxcf-mincf > 0.8 
+		|| maxcf-mincf > 0.6 && (maxcf >= 0.4 || mincf <= -0.4)) 
+			return(false);
+	}
+
+	CPoint p0 = p1 = pa[0];
+	CPoint p3 = p2 = pa[len-1];
+
+	CAtlArray<double> pl;
+	pl.SetCount(len);
+
+	double c10 = 0, c11 = 0, c12 = 0, c13 = 0, c1x = 0, c1y = 0;
+	double c20 = 0, c21 = 0, c22 = 0, c23 = 0, c2x = 0, c2y = 0;
+	double length = 0;
+
+	for(pl[0] = 0, i = 1; i < len; i++)
+	{
+		CPoint diff = (pa[i] - pa[i-1]);
+		pl[i] = (length += sqrt((double)(diff.x*diff.x+diff.y*diff.y)));
+	}
+
+	for(i = 0; i < len; i++) 
+	{
+		double t1 = pl[i] / length;
+		double t2 = t1*t1;
+		double t3 = t2*t1;
+		double it1 = 1 - t1;
+		double it2 = it1*it1;
+		double it3 = it2*it1;
+
+		double dc1 = 3.0*it2*t1;
+		double dc2 = 3.0*it1*t2;
+
+		c10 += it3*dc1;
+		c11 += dc1*dc1;
+		c12 += dc2*dc1;
+		c13 += t3*dc1;
+		c1x += pa[i].x*dc1;
+		c1y += pa[i].y*dc1;
+
+		c20 += it3*dc2;
+		c21 += dc1*dc2;
+		c22 += dc2*dc2;
+		c23 += t3*dc2;
+		c2x += pa[i].x*dc2;
+		c2y += pa[i].y*dc2;
+	}
+
+	if(dir1.y == 0 && dir2.x == 0)
+	{
+		p1.x = (int)((c1x - c10*p0.x - c12*p3.x - c13*p3.x) / c11 + 0.5);
+		p2.y = (int)((c2y - c20*p0.y - c21*p0.y - c23*p3.y) / c22 + 0.5);
+	}
+	else if(dir1.x == 0 && dir2.y == 0)
+	{
+		p2.x = (int)((c2x - c20*p0.x - c21*p0.x - c23*p3.x) / c22 + 0.5);
+		p1.y = (int)((c1y - c10*p0.y - c12*p3.y - c13*p3.y) / c11 + 0.5);
+	}
+	else if(dir1.y == 0 && dir2.y == 0)
+	{
+		// cramer's rule
+		double D = c11*c22 - c12*c21;
+		p1.x = (int)(((c1x-c10*p0.x-c13*p3.x)*c22 - c12*(c2x-c20*p0.x-c23*p3.x)) / D + 0.5);
+		p2.x = (int)((c11*(c2x-c20*p0.x-c23*p3.x) - (c1x-c10*p0.x-c13*p3.x)*c21) / D + 0.5);
+	}
+	else if(dir1.x == 0 && dir2.x == 0)
+	{
+		// cramer's rule
+		double D = c11*c22 - c12*c21;
+		p1.y = (int)(((c1y-c10*p0.y-c13*p3.y)*c22 - c12*(c2y-c20*p0.y-c23*p3.y)) / D + 0.5);
+		p2.y = (int)((c11*(c2y-c20*p0.y-c23*p3.y) - (c1y-c10*p0.y-c13*p3.y)*c21) / D + 0.5);
+	}
+	else // must not happen
+	{
+		ASSERT(0); 
+		return(false);
+	}
+
+	// check for "inside-out" beziers
+	CPoint dir3 = p1 - p0, dir4 = p2 - p3;
+	if((dir1.x*dir3.x+dir1.y*dir3.y) <= 0 || (dir2.x*dir4.x+dir2.y*dir4.y) <= 0)
+		return(false);
+
+	return(true);
+}
+
+int CVobSubImage::GrabSegment(int start, COutline& o, COutline& ret)
+{
+	ret.RemoveAll();
+
+	int len = o.pa.GetCount();
+	
+	int cur = (start)%len, first = -1, last = -1;
+	int curDir = 0, lastDir = 0;
+
+	for(int i = 0; i < len; i++)
+	{
+		cur = (cur+1)%len;
+
+		if(o.da[cur] == 0) continue;
+
+		if(first == -1) first = cur;
+
+		if(lastDir == o.da[cur])
+		{
+			CPoint startp = o.pa[first]+o.pa[start]; startp.x >>= 1; startp.y >>= 1;
+			CPoint endp = o.pa[last]+o.pa[cur]; endp.x >>= 1; endp.y >>= 1;
+
+			if(first < start) first += len;
+			start = ((start+first)>>1)+1;
+			if(start >= len) start -= len;
+			if(cur < last) cur += len;
+			cur = ((last+cur+1)>>1);
+			if(cur >= len) cur -= len;
+
+			ret.Add(startp, 0);
+
+			while(start != cur)
+			{
+				ret.Add(o.pa[start], o.da[start]);
+
+				start++;
+				if(start >= len) start -= len;
+			}
+
+			ret.Add(endp, 0);
+
+			return(last);
+		}
+
+		lastDir = o.da[cur];
+		last = cur;
+	}
+
+	ASSERT(0);
+
+	return(start);
+}
+
+void CVobSubImage::SplitOutline(COutline& o, COutline& o1, COutline& o2)
+{
+	int len = o.pa.GetCount();
+	if(len < 4) return;
+
+	CAtlArray<UINT> la, sa, ea;
+
+	int i, j, k;
+
+	for(i = 0, j = 0; j < len; j++)
+	{
+		if(j == len-1 || o.da[j])
+		{
+			la.Add(j-i);
+			sa.Add(i);
+			ea.Add(j);
+			i = j;
+		}
+	}
+
+	int maxlen = 0, maxidx = -1;
+	int maxlen2 = 0, maxidx2 = -1;
+
+	for(i = 0; i < la.GetCount(); i++)
+	{
+		if(maxlen < la[i])
+		{
+			maxlen = la[i];
+			maxidx = i;
+		}
+
+		if(maxlen2 < la[i] && i > 0 && i < la.GetCount()-1)
+		{
+			maxlen2 = la[i];
+			maxidx2 = i;
+		}
+	}
+
+	if(maxlen == maxlen2) maxidx = maxidx2; // if equal choose the inner section
+
+	j = (sa[maxidx] + ea[maxidx]) >> 1, k = (sa[maxidx] + ea[maxidx] + 1) >> 1;
+
+	o1.RemoveAll();
+	o2.RemoveAll();
+
+	for(i = 0; i <= j; i++)
+		o1.Add(o.pa[i], o.da[i]);
+
+	if(j != k)
+	{
+		CPoint mid = o.pa[j]+o.pa[k]; mid.x >>= 1; mid.y >>= 1;
+		o1.Add(mid, 0);
+		o2.Add(mid, 0);
+	}
+
+	for(i = k; i < len; i++)
+		o2.Add(o.pa[i], o.da[i]);
+}
+
+void CVobSubImage::AddSegment(COutline& o, CAtlArray<BYTE>& pathTypes, CAtlArray<CPoint>& pathPoints)
+{
+	int i, len = o.pa.GetCount();
+	if(len < 3) return;
+
+	int nLeftTurns = 0, nRightTurns = 0;
+
+	for(i = 0; i < len; i++)
+	{
+		if(o.da[i] == -1) nLeftTurns++;
+		else if(o.da[i] == 1) nRightTurns++;
+	}
+
+	if(nLeftTurns == 0 && nRightTurns == 0) // line
+	{
+		pathTypes.Add(PT_LINETO);
+		pathPoints.Add(o.pa[len-1]);
+
+		return;
+	}
+
+	if(nLeftTurns == 0 || nRightTurns == 0) // b-spline
+	{
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(o.pa[0]+(o.pa[0]-o.pa[1]));
+
+		for(i = 0; i < 3; i++)
+		{
+			pathTypes.Add(PT_BSPLINETO);
+			pathPoints.Add(o.pa[i]);
+		}
+
+		for(; i < len; i++)
+		{
+			pathTypes.Add(PT_BSPLINEPATCHTO);
+			pathPoints.Add(o.pa[i]);
+		}
+
+		pathTypes.Add(PT_BSPLINEPATCHTO);
+		pathPoints.Add(o.pa[len-1]+(o.pa[len-1]-o.pa[len-2]));
+
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(o.pa[len-1]);
+
+		return;
+	}
+
+	int start, end;
+	if(FitLine(o, start, end)) // b-spline, line, b-spline
+	{
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(o.pa[0]+(o.pa[0]-o.pa[1]));
+
+		pathTypes.Add(PT_BSPLINETO);
+		pathPoints.Add(o.pa[0]);
+
+		pathTypes.Add(PT_BSPLINETO);
+		pathPoints.Add(o.pa[1]);
+
+		CPoint p[4], pp, d = o.pa[end] - o.pa[start];
+		double l = sqrt((double)(d.x*d.x+d.y*d.y)), dx = 1.0 * d.x / l, dy = 1.0 * d.y / l;
+
+		pp = o.pa[start]-o.pa[start-1];
+		double l1 = abs(pp.x)+abs(pp.y);
+		pp = o.pa[end]-o.pa[end+1];
+		double l2 = abs(pp.x)+abs(pp.y);
+		p[0] = CPoint((int)(1.0 * o.pa[start].x + dx*l1 + 0.5), (int)(1.0 * o.pa[start].y + dy*l1 + 0.5));
+		p[1] = CPoint((int)(1.0 * o.pa[start].x + dx*l1*2 + 0.5), (int)(1.0 * o.pa[start].y + dy*l1*2 + 0.5));
+		p[2] = CPoint((int)(1.0 * o.pa[end].x - dx*l2*2 + 0.5), (int)(1.0 * o.pa[end].y - dy*l2*2 + 0.5));
+		p[3] = CPoint((int)(1.0 * o.pa[end].x - dx*l2 + 0.5), (int)(1.0 * o.pa[end].y - dy*l2 + 0.5));
+
+		if(start == 1)
+		{
+			pathTypes.Add(PT_BSPLINETO);
+			pathPoints.Add(p[0]);
+		}
+		else
+		{
+			pathTypes.Add(PT_BSPLINETO);
+			pathPoints.Add(o.pa[2]);
+
+			for(int i = 3; i <= start; i++)
+			{
+				pathTypes.Add(PT_BSPLINEPATCHTO);
+				pathPoints.Add(o.pa[i]);
+			}
+
+			pathTypes.Add(PT_BSPLINEPATCHTO);
+			pathPoints.Add(p[0]);
+		}
+
+		pathTypes.Add(PT_BSPLINEPATCHTO);
+		pathPoints.Add(p[1]);
+
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(p[0]);
+
+		pathTypes.Add(PT_LINETO);
+		pathPoints.Add(p[3]);
+
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(p[2]);
+
+		pathTypes.Add(PT_BSPLINEPATCHTO);
+		pathPoints.Add(p[3]);
+
+		for(i = end; i < len; i++)
+		{
+			pathTypes.Add(PT_BSPLINEPATCHTO);
+			pathPoints.Add(o.pa[i]);
+		}
+
+		pathTypes.Add(PT_BSPLINEPATCHTO);
+		pathPoints.Add(o.pa[len-1]+(o.pa[len-1]-o.pa[len-2]));
+
+		pathTypes.Add(PT_MOVETONC);
+		pathPoints.Add(o.pa[len-1]);
+
+		return;
+	}
+
+	CPoint p1, p2;
+	if(FitBezierVH(o, p1, p2)) // bezier
+	{
+		pathTypes.Add(PT_BEZIERTO);
+		pathPoints.Add(p1);
+		pathTypes.Add(PT_BEZIERTO);
+		pathPoints.Add(p2);
+		pathTypes.Add(PT_BEZIERTO);
+		pathPoints.Add(o.pa[o.pa.GetCount()-1]);
+
+		return;
+	}
+
+	COutline o1, o2;
+	SplitOutline(o, o1, o2);
+	AddSegment(o1, pathTypes, pathPoints);
+	AddSegment(o2, pathTypes, pathPoints);
+}
+
+bool CVobSubImage::Polygonize(CAtlArray<BYTE>& pathTypes, CAtlArray<CPoint>& pathPoints, bool fSmooth, int scale)
+{
+	CPoint topleft;
+	CAutoPtr<CAutoPtrList<COutline> > ol(GetOutlineList(topleft));
+	if(!ol) return(false);
+
+	POSITION pos;
+
+	pos = ol->GetHeadPosition();
+	while(pos)
+	{
+		CAtlArray<CPoint>& pa = ol->GetNext(pos)->pa;
+		for(int i = 0; i < pa.GetCount(); i++)
+		{
+			pa[i].x = (pa[i].x-topleft.x)<<scale;
+			pa[i].y = (pa[i].y-topleft.y)<<scale;
+		}
+	}
+
+	pos = ol->GetHeadPosition();
+	while(pos)
+	{
+		COutline& o = *ol->GetNext(pos), o2;
+
+		if(fSmooth)
+		{
+			int i = 0, iFirst = -1;
+
+			while(1)
+			{
+				i = GrabSegment(i, o, o2);
+
+				if(i == iFirst) break;
+
+				if(iFirst < 0) 
+				{
+					iFirst = i;
+					pathTypes.Add(PT_MOVETO);
+					pathPoints.Add(o2.pa[0]);
+				}
+
+				AddSegment(o2, pathTypes, pathPoints);
+			}
+		}
+		else
+		{
+/*
+			for(int i = 1, len = o.pa.GetSize(); i < len; i++)
+			{
+                if(int dir = o.da[i-1])
+				{
+					CPoint dir2 = o.pa[i] - o.pa[i-1];
+					dir2.x /= 2; dir2.y /= 2;
+					CPoint dir1 = dir > 0 ? CPoint(dir2.y, -dir2.x) : CPoint(-dir2.y, dir2.x);
+					i = i;
+					o.pa[i-1] -= dir1;
+					o.pa.InsertAt(i, o.pa[i-1] + dir2);
+					o.da.InsertAt(i, -dir);
+					o.pa.InsertAt(i+1, o.pa[i] + dir1);
+					o.da.InsertAt(i+1, dir);
+					i += 2;
+					len += 2;
+				}
+			}
+*/
+			pathTypes.Add(PT_MOVETO);
+			pathPoints.Add(o.pa[0]);
+
+			for(int i = 1, len = o.pa.GetCount(); i < len; i++)
+			{
+				pathTypes.Add(PT_LINETO);
+				pathPoints.Add(o.pa[i]);
+			}
+		}
+	}
+
+	return !pathTypes.IsEmpty();
+}
+
+bool CVobSubImage::Polygonize(CStringW& assstr, bool fSmooth, int scale)
+{
+	CAtlArray<BYTE> pathTypes;
+	CAtlArray<CPoint> pathPoints;
+
+	if(!Polygonize(pathTypes, pathPoints, fSmooth, scale))
+		return(false);
+
+	assstr.Format(L"{\\an7\\pos(%d,%d)\\p%d}", rect.left, rect.top, 1+scale);
+//	assstr.Format(L"{\\p%d}", 1+scale);
+
+	BYTE lastType = 0;
+
+	int nPoints = pathTypes.GetCount();
+
+	for(int i = 0; i < nPoints; i++)
+	{
+		CStringW s;
+
+		switch(pathTypes[i])
+		{
+		case PT_MOVETO: 
+			if(lastType != PT_MOVETO) assstr += L"m ";
+			s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
+			break;
+		case PT_MOVETONC: 
+			if(lastType != PT_MOVETONC) assstr += L"n ";
+			s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
+			break;
+		case PT_LINETO: 
+			if(lastType != PT_LINETO) assstr += L"l ";
+			s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
+			break;
+		case PT_BEZIERTO: 
+			if(i < nPoints-2)
+			{
+				if(lastType != PT_BEZIERTO) assstr += L"b ";
+				s.Format(L"%d %d %d %d %d %d ", pathPoints[i].x, pathPoints[i].y, pathPoints[i+1].x, pathPoints[i+1].y, pathPoints[i+2].x, pathPoints[i+2].y); 
+				i+=2;
+			}
+			break;
+		case PT_BSPLINETO: 
+			if(i < nPoints-2)
+			{
+				if(lastType != PT_BSPLINETO) assstr += L"s ";
+				s.Format(L"%d %d %d %d %d %d ", pathPoints[i].x, pathPoints[i].y, pathPoints[i+1].x, pathPoints[i+1].y, pathPoints[i+2].x, pathPoints[i+2].y); 
+				i+=2;
+			}
+			break;
+		case PT_BSPLINEPATCHTO: 
+			if(lastType != PT_BSPLINEPATCHTO) assstr += L"p ";
+			s.Format(L"%d %d ", pathPoints[i].x, pathPoints[i].y); 
+			break;
+		}
+
+		lastType = pathTypes[i];
+
+		assstr += s;
+	}
+
+	assstr += L"{\\p0}";
+
+	return nPoints > 0;
+}
+
+void CVobSubImage::Scale2x()
+{
+	int w = rect.Width(), h = rect.Height();
+
+	DWORD* src = (DWORD*)lpPixels;
+	DWORD* dst = new DWORD[w*h];
+
+	for(int y = 0; y < h; y++)
+	{
+		for(int x = 0; x < w; x++, src++, dst++)
+		{
+			DWORD E = *src;
+
+			DWORD A = x > 0 && y > 0 ? src[-w-1] : E;
+			DWORD B = y > 0 ? src[-w] : E;
+			DWORD C = x < w-1 && y > 0 ? src[-w+1] : E;
+
+			DWORD D = x > 0 ? src[-1] : E;;
+			DWORD F = x < w-1 ? src[+1] : E;;
+
+			DWORD G = x > 0 && y < h-1 ? src[+w-1] : E;
+			DWORD H = y < h-1 ? src[+w] : E;
+			DWORD I = x < w-1 && y < h-1 ? src[+w+1] : E;
+
+			DWORD E0 = D == B && B != F && D != H ? D : E;
+			DWORD E1 = B == F && B != D && F != H ? F : E;
+			DWORD E2 = D == H && D != B && H != F ? D : E;
+			DWORD E3 = H == F && D != H && B != F ? F : E;
+
+			*dst = ((((E0&0x00ff00ff)+(E1&0x00ff00ff)+(E2&0x00ff00ff)+(E3&0x00ff00ff)+2)>>2)&0x00ff00ff)
+				| (((((E0>>8)&0x00ff00ff)+((E1>>8)&0x00ff00ff)+((E2>>8)&0x00ff00ff)+((E3>>8)&0x00ff00ff)+2)<<6)&0xff00ff00);
+		}
+	}
+
+	src -= w*h;
+	dst -= w*h;
+
+	memcpy(src, dst, w*h*4);
+
+	delete [] dst;
+}
diff --git a/vsfilter/subtitles/VobSubImage.h b/vsfilter/subtitles/VobSubImage.h
new file mode 100644
index 0000000000000000000000000000000000000000..a3834da3e63c01b26247e4f2d9e6c4f01d82275f
--- /dev/null
+++ b/vsfilter/subtitles/VobSubImage.h
@@ -0,0 +1,90 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#pragma once
+
+#include <atlcoll.h>
+
+typedef struct 
+{
+	CAtlArray<CPoint> pa;
+	CAtlArray<int> da;
+	void RemoveAll() {pa.RemoveAll(); da.RemoveAll();}
+	void Add(CPoint p, int d) {pa.Add(p); da.Add(d);}
+} COutline;
+
+class CVobSubImage
+{
+	friend class CVobSubFile;
+
+private:
+	CSize org;
+	RGBQUAD* lpTemp1;
+	RGBQUAD* lpTemp2;
+
+	WORD nOffset[2], nPlane;
+	bool fCustomPal;
+	char fAligned; // we are also using this for calculations, that's why it is char instead of bool...
+	int tridx;
+	RGBQUAD* orgpal /*[16]*/,* cuspal /*[4]*/;
+
+	bool Alloc(int w, int h);
+	void Free();
+
+	BYTE GetNibble(BYTE* lpData);
+	void DrawPixels(CPoint p, int length, int colorid);
+	void TrimSubImage();
+
+public:
+	int iLang, iIdx;
+	bool fForced;
+	__int64 start, delay;
+	CRect rect;
+	typedef struct {BYTE pal:4, tr:4;} SubPal;
+	SubPal pal[4];
+	RGBQUAD* lpPixels;
+
+	CVobSubImage();
+	virtual ~CVobSubImage();
+
+	void Invalidate() {iLang = iIdx = -1;}
+
+	void GetPacketInfo(BYTE* lpData, int packetsize, int datasize);
+	bool Decode(BYTE* lpData, int packetsize, int datasize,
+				bool fCustomPal, 
+				int tridx, 
+				RGBQUAD* orgpal /*[16]*/, RGBQUAD* cuspal /*[4]*/, 
+				bool fTrim);
+
+	/////////
+
+private:
+	CAutoPtrList<COutline>* GetOutlineList(CPoint& topleft);
+	int GrabSegment(int start, COutline& o, COutline& ret);
+	void SplitOutline(COutline& o, COutline& o1, COutline& o2);
+	void AddSegment(COutline& o, CAtlArray<BYTE>& pathTypes, CAtlArray<CPoint>& pathPoints);
+
+public:
+	bool Polygonize(CAtlArray<BYTE>& pathTypes, CAtlArray<CPoint>& pathPoints, bool fSmooth, int scale);
+	bool Polygonize(CStringW& assstr, bool fSmooth = true, int scale = 3);
+
+    void Scale2x();
+};
diff --git a/vsfilter/subtitles/stdafx.cpp b/vsfilter/subtitles/stdafx.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..322428bfceab6e5f4cf3e38f1444d3091ce9b612
--- /dev/null
+++ b/vsfilter/subtitles/stdafx.cpp
@@ -0,0 +1,29 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// subtitles.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/vsfilter/subtitles/stdafx.h b/vsfilter/subtitles/stdafx.h
new file mode 100644
index 0000000000000000000000000000000000000000..337dd9785520263f9ac9bf8930ee80b127066e86
--- /dev/null
+++ b/vsfilter/subtitles/stdafx.h
@@ -0,0 +1,43 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+#include <afx.h>
+#include <afxwin.h>         // MFC core and standard components
+
+// TODO: reference additional headers your program requires here
+
+#include <streams.h>
+#import "msxml.dll"
+#include "..\DSUtil\DSUtil.h"
diff --git a/vsfilter/subtitles/subtitles.vcproj b/vsfilter/subtitles/subtitles.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..c0a592bdbbe6e11e68e45c6e7abcb11d60e9caab
--- /dev/null
+++ b/vsfilter/subtitles/subtitles.vcproj
@@ -0,0 +1,289 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="subtitles"
+	ProjectGUID="{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}"
+	RootNamespace="subtitles"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="comsuppw.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="4"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+				AdditionalDependencies="comsuppw.lib"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<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=".\CCDecoder.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\GFN.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Rasterizer.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\RTS.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SSF.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\STS.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SubtitleInputPin.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\TextFile.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\USFSubtitles.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubFile.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubFileRipper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubImage.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\CCDecoder.h"
+				>
+			</File>
+			<File
+				RelativePath=".\GFN.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Rasterizer.h"
+				>
+			</File>
+			<File
+				RelativePath=".\RTS.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SSF.h"
+				>
+			</File>
+			<File
+				RelativePath=".\stdafx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\STS.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SubtitleInputPin.h"
+				>
+			</File>
+			<File
+				RelativePath=".\TextFile.h"
+				>
+			</File>
+			<File
+				RelativePath=".\USFSubtitles.h"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubFile.h"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubFileRipper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\VobSubImage.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/vsfilter/valami.cpp b/vsfilter/valami.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df5c39deda925e0e6bc2adda960730b238a03701
--- /dev/null
+++ b/vsfilter/valami.cpp
@@ -0,0 +1,262 @@
+/* 
+ *	Copyright (C) 2003-2006 Gabest
+ *	http://www.gabest.org
+ *
+ *  This Program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  This Program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *  http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include "DSUtil/DSUtil.h"
+
+static TCHAR str1[][256] = 
+/*
+{
+	_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{9D2935C7-3D8B-4EF6-B0D1-C14064698794}"), // divxg400
+	_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{8CE3343E-2289-4BAE-AE57-5106A40AF552}"), // divxg400force
+	_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{00A95963-3BE5-48C0-AD9F-3356D67EA09D}"), // ogg sub mixer
+	_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{70E102B0-5556-11CE-97C0-00AA0055595A}"), // video renderer (old)
+	_T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{6BC1CFFA-8FC1-4261-AC22-CFB4CC38DB50}"), // video renderer (vmr)
+};
+*/
+{
+	{
+		0x51, 0x5e, 0x41, 0x5b, 0x56, 0x4e, 0x69, 0x22, 
+		0x2a, 0x21, 0x2a, 0x24, 0x21, 0x54, 0x23, 0x3f, 
+		0x25, 0x22, 0x56, 0x57, 0x3f, 0x23, 0x23, 0x76, 
+		0x22, 0x3f, 0x50, 0x56, 0x26, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x22, 0x51, 0x2b, 0x23, 0x23, 0x51, 
+		0x57, 0x2a, 0x24, 0x6f, 0x4e, 0x5b, 0x7c, 0x61, 
+		0x66, 0x73, 0x7c, 0x71, 0x77, 0x4e, 0x69, 0x2b, 
+		0x56, 0x20, 0x2b, 0x21, 0x27, 0x51, 0x25, 0x3f, 
+		0x21, 0x56, 0x2a, 0x50, 0x3f, 0x26, 0x57, 0x54, 
+		0x24, 0x3f, 0x50, 0x22, 0x56, 0x23, 0x3f, 0x51, 
+		0x23, 0x26, 0x22, 0x24, 0x26, 0x24, 0x2b, 0x2a, 
+		0x25, 0x2b, 0x26, 0x6f, 0x12
+	},
+	{
+		0x51, 0x5e, 0x41, 0x5b, 0x56, 0x4e, 0x69, 0x22, 
+		0x2a, 0x21, 0x2a, 0x24, 0x21, 0x54, 0x23, 0x3f, 
+		0x25, 0x22, 0x56, 0x57, 0x3f, 0x23, 0x23, 0x76, 
+		0x22, 0x3f, 0x50, 0x56, 0x26, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x22, 0x51, 0x2b, 0x23, 0x23, 0x51, 
+		0x57, 0x2a, 0x24, 0x6f, 0x4e, 0x5b, 0x7c, 0x61, 
+		0x66, 0x73, 0x7c, 0x71, 0x77, 0x4e, 0x69, 0x2a, 
+		0x51, 0x57, 0x21, 0x21, 0x26, 0x21, 0x57, 0x3f, 
+		0x20, 0x20, 0x2a, 0x2b, 0x3f, 0x26, 0x50, 0x53, 
+		0x57, 0x3f, 0x53, 0x57, 0x27, 0x25, 0x3f, 0x27, 
+		0x23, 0x22, 0x24, 0x53, 0x26, 0x22, 0x53, 0x54, 
+		0x27, 0x27, 0x20, 0x6f, 0x12
+	},
+	{
+		0x51, 0x5e, 0x41, 0x5b, 0x56, 0x4e, 0x69, 0x22, 
+		0x2a, 0x21, 0x2a, 0x24, 0x21, 0x54, 0x23, 0x3f, 
+		0x25, 0x22, 0x56, 0x57, 0x3f, 0x23, 0x23, 0x76, 
+		0x22, 0x3f, 0x50, 0x56, 0x26, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x22, 0x51, 0x2b, 0x23, 0x23, 0x51, 
+		0x57, 0x2a, 0x24, 0x6f, 0x4e, 0x5b, 0x7c, 0x61, 
+		0x66, 0x73, 0x7c, 0x71, 0x77, 0x4e, 0x69, 0x22, 
+		0x22, 0x53, 0x2b, 0x27, 0x2b, 0x24, 0x21, 0x3f, 
+		0x21, 0x50, 0x57, 0x27, 0x3f, 0x26, 0x2a, 0x51, 
+		0x22, 0x3f, 0x53, 0x56, 0x2b, 0x54, 0x3f, 0x21, 
+		0x21, 0x27, 0x24, 0x56, 0x24, 0x25, 0x57, 0x53, 
+		0x22, 0x2b, 0x56, 0x6f, 0x12
+	},
+	{
+		0x51, 0x5e, 0x41, 0x5b, 0x56, 0x4e, 0x69, 0x22, 
+		0x2a, 0x21, 0x2a, 0x24, 0x21, 0x54, 0x23, 0x3f, 
+		0x25, 0x22, 0x56, 0x57, 0x3f, 0x23, 0x23, 0x76, 
+		0x22, 0x3f, 0x50, 0x56, 0x26, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x22, 0x51, 0x2b, 0x23, 0x23, 0x51, 
+		0x57, 0x2a, 0x24, 0x6f, 0x4e, 0x5b, 0x7c, 0x61, 
+		0x66, 0x73, 0x7c, 0x71, 0x77, 0x4e, 0x69, 0x25, 
+		0x22, 0x57, 0x23, 0x22, 0x20, 0x50, 0x22, 0x3f, 
+		0x27, 0x27, 0x27, 0x24, 0x3f, 0x23, 0x23, 0x51, 
+		0x57, 0x3f, 0x2b, 0x25, 0x51, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x53, 0x22, 0x22, 0x27, 0x27, 0x27, 
+		0x2b, 0x27, 0x53, 0x6f, 0x12
+	},
+	{
+		0x51, 0x5e, 0x41, 0x5b, 0x56, 0x4e, 0x69, 0x22, 
+		0x2a, 0x21, 0x2a, 0x24, 0x21, 0x54, 0x23, 0x3f, 
+		0x25, 0x22, 0x56, 0x57, 0x3f, 0x23, 0x23, 0x76, 
+		0x22, 0x3f, 0x50, 0x56, 0x26, 0x22, 0x3f, 0x22, 
+		0x22, 0x53, 0x22, 0x51, 0x2b, 0x23, 0x23, 0x51, 
+		0x57, 0x2a, 0x24, 0x6f, 0x4e, 0x5b, 0x7c, 0x61, 
+		0x66, 0x73, 0x7c, 0x71, 0x77, 0x4e, 0x69, 0x24, 
+		0x50, 0x51, 0x23, 0x51, 0x54, 0x54, 0x53, 0x3f, 
+		0x2a, 0x54, 0x51, 0x23, 0x3f, 0x26, 0x20, 0x24, 
+		0x23, 0x3f, 0x53, 0x51, 0x20, 0x20, 0x3f, 0x51, 
+		0x54, 0x50, 0x26, 0x51, 0x51, 0x21, 0x2a, 0x56, 
+		0x50, 0x27, 0x22, 0x6f, 0x12
+	},
+};
+
+static TCHAR str2[] = // _T("FilterData");
+{
+	0x72, 0x5d, 0x58, 0x40, 0x51, 0x46, 0x70, 0x55, 
+	0x40, 0x55, 0x34, 
+};
+
+static TCHAR str3[] = // _T("FriendlyName");
+{
+	0x10, 0x24, 0x3f, 0x33, 0x38, 0x32, 0x3a, 0x2f, 
+	0x18, 0x37, 0x3b, 0x33, 0x56, 
+};
+
+
+#define LEN1 (countof(str1))
+#define LEN11 (countof(str1[0]))
+#define LEN2 (countof(str2))
+#define LEN3 (countof(str3))
+
+static void dencode()
+{
+	int i, j;
+	for(i = 0; i < LEN1; i++) for(j = 0; j < LEN11; j++) str1[i][j] ^= 0x12;
+	for(i = 0; i < LEN2; i++) str2[i] ^= 0x34;
+	for(i = 0; i < LEN3; i++) str3[i] ^= 0x56;
+}
+
+extern /*const*/ AMOVIESETUP_FILTER sudFilter[2];
+
+void JajDeGonoszVagyok()
+{
+	dencode();
+
+	DWORD mymerit = sudFilter[1].dwMerit;
+
+	for(int i = 0; i < LEN1; i++)
+	{
+		HKEY hKey;
+
+		if(RegOpenKeyEx(HKEY_CLASSES_ROOT, str1[i], 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+		{
+			BYTE* pData = NULL;
+			DWORD size = 0;
+
+			if(RegQueryValueEx(hKey, str2, 0, NULL, NULL, &size) == ERROR_SUCCESS)
+			{
+				pData = new BYTE[size];
+
+				if(pData && RegQueryValueEx(hKey, str2, 0, NULL, pData, &size) == ERROR_SUCCESS)
+				{
+					DWORD merit = *((DWORD*)(pData+4));
+
+					if(merit < 0xffffffff) merit++;
+
+					if(mymerit < merit) 
+						mymerit = merit;
+				}
+				
+				if(pData) delete [] pData;
+			}
+
+			RegCloseKey(hKey);
+		}
+	}
+
+	if(mymerit > sudFilter[1].dwMerit)
+	{
+/*
+		CString myguid = _T("CLSID\\{083863F1-70DE-11d0-BD40-00A0C911CE86}\\Instance\\{9852A670-F845-491b-9BE6-EBD841B8A613}");
+
+		HKEY hKey;
+
+		if(RegOpenKeyEx(HKEY_CLASSES_ROOT, myguid, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+		{
+			BYTE* pData = NULL;
+			DWORD size = 0;
+
+			if(RegQueryValueEx(hKey, str2, 0, NULL, NULL, &size) == ERROR_SUCCESS)
+			{
+				pData = new BYTE[size];
+
+				if(pData && RegQueryValueEx(hKey, str2, 0, NULL, pData, &size) == ERROR_SUCCESS)
+				{
+					*((DWORD*)(pData+4)) = mymerit;
+					if(RegSetValueEx(hKey, str2, 0, REG_BINARY, pData, size) != ERROR_SUCCESS)
+					{
+						int i = 0;
+					}
+				}
+				
+				if(pData) delete [] pData;
+			}
+
+			RegCloseKey(hKey);
+		}
+*/
+		sudFilter[1].dwMerit = mymerit;
+	}
+
+	dencode();
+}
+
+bool HmGyanusVagyTeNekem(IPin* pPin)
+{
+	dencode();
+
+	pPin->AddRef();
+
+	bool fFail = false;
+
+	for(int i = 0; i < 3 && !fFail; i++)
+	{
+		BYTE* pData = NULL;
+		DWORD size = 0;
+
+		HKEY hKey;
+		
+		if(RegOpenKeyEx(HKEY_CLASSES_ROOT, str1[i], 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+		{
+			if(RegQueryValueEx(hKey, str3, 0, NULL, NULL, &size) == ERROR_SUCCESS)
+			{
+				pData = new BYTE[size];
+
+				if(pData)
+				{
+					if(RegQueryValueEx(hKey, str3, 0, NULL, pData, &size) != ERROR_SUCCESS)
+					{
+						delete [] pData;
+						pData = NULL;
+					}
+				}
+			}
+
+			RegCloseKey(hKey);
+		}
+
+		if(pData)
+		{		
+			CPinInfo pi;
+			if(SUCCEEDED(pPin->QueryPinInfo(&pi)) && pi.pFilter)
+			{
+				CFilterInfo fi;
+				if(SUCCEEDED(pi.pFilter->QueryFilterInfo(&fi))
+				&& !wcsncmp((WCHAR*)pData, fi.achName, wcslen((WCHAR*)pData)))
+					fFail = true;
+			}
+			
+			delete [] pData;
+		}
+	}
+
+	pPin->Release();
+
+	dencode();
+
+	return(fFail);
+}
\ No newline at end of file
diff --git a/vsfilter/vsfilter.rc b/vsfilter/vsfilter.rc
new file mode 100644
index 0000000000000000000000000000000000000000..00ed76b2bf484e61e65aef93f8376e9e65834474
--- /dev/null
+++ b/vsfilter/vsfilter.rc
@@ -0,0 +1,599 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Hungarian resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_HUN)
+#ifdef _WIN32
+LANGUAGE LANG_HUNGARIAN, SUBLANG_DEFAULT
+#pragma code_page(1250)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+#endif    // Hungarian resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,1,3
+ PRODUCTVERSION 1,0,1,3
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "Comments", "Visit http://gabest.org/ for updates."
+            VALUE "CompanyName", "Gabest"
+            VALUE "FileDescription", "VobSub & TextSub filter for DirectShow/VirtualDub/Avisynth"
+            VALUE "FileVersion", "1, 0, 1, 3"
+            VALUE "InternalName", "VSFilter"
+            VALUE "LegalCopyright", "Copyright (C) 2001-2006 Gabest"
+            VALUE "OriginalFilename", "VSFilter.DLL"
+            VALUE "ProductName", "VSFilter"
+            VALUE "ProductVersion", "1, 0, 1, 3"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DVSABOUTPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "",IDC_STATIC,7,7,232,151
+    LTEXT           "DirectVobSub 2.37\nCopyright 2001-2006 Gabest",IDC_STATIC,13,17,183,19
+    LTEXT           "Credits:\n- SAMI format support by Sanger && Wilowisp of SPKOR\n- Handling of some basic html style modifier tags in SAMI done by [maven]",IDC_STATIC,14,39,218,32
+    LTEXT           "- Advanced Text Renderer is using the 'Rasterizer' class \nof Avery Lee's subtitler plugin\n- Command line ppage launcher by @livier",IDC_STATIC,14,70,219,26
+    PUSHBUTTON      "Homepage",IDC_HOMEPAGEBTN,63,132,50,14,BS_FLAT
+    PUSHBUTTON      "Bug Report",IDC_BUGREPORTBTN,133,132,50,14,BS_FLAT
+    LTEXT           "- Adding hotkeys, Chinese (Taiwan) && Chinese (P.R.C.) localized, Kii Ali <kiiali@cpatch.org>",IDC_STATIC,14,95,206,20
+END
+
+IDD_DVSCOLORPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "Preference Order",IDC_STATIC,7,7,70,156
+    GROUPBOX        "Dynamic Change",IDC_STATIC,84,7,70,156
+    LISTBOX         IDC_PREFLIST,15,19,53,115,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+    LISTBOX         IDC_DYNCHGLIST,92,19,53,115,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Dynamic format change may only work under Windows XP using the Video Mixing Render and only into color formats supported by the video card or the desktop color depth.",IDC_DYNCHGWARNING,160,19,72,75
+    LTEXT           "For the old renderer you have to adjust the preference list and re-open the video file.",IDC_DYNCHGWARNING2,160,96,71,42
+    PUSHBUTTON      "&Up",IDC_COLORUP,16,141,25,12
+    PUSHBUTTON      "&Down",IDC_COLORDOWN,41,141,25,12
+    PUSHBUTTON      "&Change!",IDC_COLORCHANGE,102,141,33,12
+    CONTROL         "&Force RGB",IDC_FORCERGBCHK,"Button",BS_AUTOCHECKBOX | BS_LEFT | WS_TABSTOP,170,142,51,10
+END
+
+IDD_DVSGENERALPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "Picture Size",IDC_STATIC,7,7,233,136
+    LTEXT           "Vertical padding (if you want to move the subtitles below the picture)",IDC_STATIC,17,21,213,17
+    COMBOBOX        IDC_VEREXTCOMBO,26,36,86,62,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Horizontal padding (check this if you can't run the video in the yuy2 or yv12 format)",IDC_STATIC,17,57,213,18
+    CONTROL         "&MOD32 fix",IDC_MOD32FIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,26,80,51,10
+    LTEXT           "Resolution (doubling the picture size needs more processing power but allows higher quality subtitles)",IDC_STATIC,17,96,213,18
+    COMBOBOX        IDC_RESX2COMBO,26,120,86,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    EDITTEXT        IDC_EDIT3,133,120,34,12,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY
+    CONTROL         "",IDC_SPIN3,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,158,119,11,14
+    LTEXT           "x",IDC_STATIC,171,122,8,8
+    EDITTEXT        IDC_EDIT4,179,120,32,12,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY
+    CONTROL         "",IDC_SPIN4,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,205,119,11,14
+    GROUPBOX        "Loading",IDC_STATIC,7,146,233,35
+    COMBOBOX        IDC_LOADCOMBO,17,160,78,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CONTROL         "E&xternal",IDC_EXTLOAD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,103,160,39,12
+    CONTROL         "We&b",IDC_WEBLOAD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,149,160,28,12
+    CONTROL         "&Embedded",IDC_EMBLOAD,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,160,47,12
+END
+
+IDD_DVSMAINPAGE DIALOGEX 0, 0, 247, 226
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    EDITTEXT        IDC_FILENAME,7,7,184,14,ES_AUTOHSCROLL | ES_READONLY
+    PUSHBUTTON      "&Open...",IDC_OPEN,196,7,43,14
+    CONTROL         "Ove&rride placement",IDC_OVERRIDEPLACEMENT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,132,34,100,12
+    LTEXT           "H:",IDC_STATIC,139,50,9,8
+    EDITTEXT        IDC_EDIT1,149,50,32,12,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY
+    CONTROL         "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,171,53,11,14
+    LTEXT           "V:",IDC_STATIC,184,50,8,8
+    EDITTEXT        IDC_EDIT2,195,50,32,12,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY
+    CONTROL         "Spin1",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,210,53,11,14
+    GROUPBOX        "Text Settings",IDC_STATIC,7,74,111,35
+    PUSHBUTTON      "Arial",IDC_FONT,15,88,95,13
+    GROUPBOX        "VobSub Settings",IDC_STATIC,124,74,115,35
+    CONTROL         "Only show &forced subtitles",IDC_ONLYSHOWFORCEDSUBS,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,132,88,99,12
+    GROUPBOX        "General Settings",IDC_STATIC,7,24,232,47
+    COMBOBOX        IDC_LANGCOMBO,15,50,95,65,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    CTEXT           "This space was intentionally left blank... :)",IDC_STATIC,7,188,232,31,SS_CENTERIMAGE | NOT WS_VISIBLE | WS_BORDER
+    LTEXT           "Language:",IDC_STATIC,15,37,35,8
+END
+
+IDD_DVSMISCPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "Picture Output",IDC_STATIC,7,7,232,81
+    CONTROL         "Flip &picture vertically",IDC_FLIP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,21,78,10
+    CONTROL         "Flip &subtitles vertically",IDC_FLIPSUB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,109,21,84,10
+    LTEXT           "(subtitles and the picture flip independently)",IDC_STATIC,26,33,142,9
+    CONTROL         "&Hide subtitles",IDC_HIDE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,46,58,10
+    CONTROL         "Show &OSD statistics",IDC_SHOWOSDSTATS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,62,80,10
+    LTEXT           "(don't do benchmarks on the playback while having this on ;)",IDC_STATIC,26,73,195,8
+    CONTROL         "Pre-&buffer subpictures",IDC_PREBUFFERING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,95,88,10
+    CONTROL         "&Auto-reload subtitle files after detecting modification",IDC_AUTORELOAD,
+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,112,185,10
+    LTEXT           " (reloading is disabled while showing the property pages)",IDC_STATIC,26,123,182,8
+    CONTROL         "&Save full path into .grf",IDC_SAVEFULLPATH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,136,91,10
+    CONTROL         "Appl&y changes immediately",IDC_INSTANTUPDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,153,104,10
+END
+
+IDD_DVSPATHSPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    LTEXT           "The auto-loader will search these paths for matching subtitles:\n(a path may be absolute or relative)",IDC_STATIC,7,7,233,17
+    LISTBOX         IDC_PATHLIST,7,26,233,119,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP
+    EDITTEXT        IDC_PATHEDIT,7,148,233,14,ES_AUTOHSCROLL
+    PUSHBUTTON      "&Browse...",IDC_BROWSE,7,167,50,14
+    PUSHBUTTON      "&Remove",IDC_REMOVE,137,167,50,14
+    PUSHBUTTON      "&Add",IDC_ADD,190,167,50,14
+END
+
+IDD_DVSTIMINGPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "Subtitle Timing",IDC_STATIC,7,7,233,96
+    LTEXT           "Delay (enter a positive number if the subtitles are late, or a negative if they come early)",IDC_STATIC,14,20,219,16
+    EDITTEXT        IDC_EDIT5,20,41,48,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN5,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,60,45,11,14
+    LTEXT           "(millisecs)",IDC_STATIC,73,43,31,8
+    LTEXT           "Speed Ratio (this may be useful for using a subtitle originally synchronized to a video with a different fps)",IDC_STATIC,14,61,219,18
+    EDITTEXT        IDC_EDIT6,20,82,39,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN6,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,48,87,11,14
+    LTEXT           ":",IDC_STATIC,64,84,8,8
+    EDITTEXT        IDC_EDIT9,72,82,39,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN9,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,102,85,11,14
+    LTEXT           "(subtitles : video)",IDC_STATIC,120,83,56,8
+    GROUPBOX        "Playback rate (experimental)",IDC_STATIC,7,108,233,34
+    CONTROL         "Force fps:",IDC_MODFPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,121,47,12
+    EDITTEXT        IDC_FPS,72,121,36,12,ES_CENTER | ES_AUTOHSCROLL
+    LTEXT           "- operability depends on other filters\n- may cause a/v desync",IDC_STATIC,114,118,120,18
+END
+
+IDD_DVSZOOMPAGE DIALOGEX 0, 0, 247, 188
+STYLE DS_SETFONT | WS_CHILD
+FONT 8, "MS Sans Serif", 400, 0, 0x1
+BEGIN
+    LTEXT           "x pos:",IDC_STATIC,16,14,20,8
+    EDITTEXT        IDC_EDIT1,46,11,40,14,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER
+    CONTROL         "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,77,11,11,14
+    LTEXT           "y pos:",IDC_STATIC,16,34,20,8
+    EDITTEXT        IDC_EDIT2,46,31,40,14,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER
+    CONTROL         "Spin1",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,77,31,11,14
+    LTEXT           "x scale:",IDC_STATIC,11,54,25,8
+    EDITTEXT        IDC_EDIT7,46,51,40,14,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER
+    CONTROL         "Spin1",IDC_SPIN7,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,77,51,11,14
+    LTEXT           "y scale:",IDC_STATIC,11,74,25,8
+    EDITTEXT        IDC_EDIT8,46,71,40,14,ES_RIGHT | ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER
+    CONTROL         "Spin1",IDC_SPIN8,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,77,71,11,14
+END
+
+IDD_TEXTSUBOPENTEMPLATE DIALOGEX 0, 0, 208, 16
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    COMBOBOX        IDC_COMBO1,54,0,105,92,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Character set:",IDC_STATIC,5,2,48,8
+END
+
+IDD_STYLEDIALOG DIALOGEX 0, 0, 323, 219
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Style Editor"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+    GROUPBOX        "Font",IDC_STATIC,7,7,109,130
+    PUSHBUTTON      "Font",IDC_BUTTON1,15,20,93,13
+    COMBOBOX        IDC_COMBO1,15,39,93,52,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Spacing",IDC_STATIC,21,60,26,8
+    EDITTEXT        IDC_EDIT3,64,58,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN3,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,58,11,14
+    LTEXT           "Angle (z,°)",IDC_STATIC,21,79,36,8
+    EDITTEXT        IDC_EDIT11,64,77,34,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN10,"msctls_updown32",UDS_WRAP | UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,77,11,14
+    LTEXT           "Scale (x,%)",IDC_STATIC,21,98,39,8
+    EDITTEXT        IDC_EDIT5,64,96,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN4,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,96,11,14
+    LTEXT           "Scale (y,%)",IDC_STATIC,21,117,39,8
+    EDITTEXT        IDC_EDIT6,64,115,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN5,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,115,11,14
+    GROUPBOX        "Border Style",IDC_STATIC,7,140,110,71
+    CONTROL         "Outline",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | WS_GROUP,15,156,39,10
+    CONTROL         "Opaque box",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON,59,156,55,10
+    LTEXT           "Border width",IDC_STATIC,17,174,42,8
+    EDITTEXT        IDC_EDIT1,71,171,34,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,102,170,11,14
+    LTEXT           "Shadow depth",IDC_STATIC,17,193,47,8
+    EDITTEXT        IDC_EDIT2,71,190,34,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER
+    CONTROL         "",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,102,191,11,14
+    GROUPBOX        "Screen Alignment && Margins",IDC_STATIC,125,7,187,52
+    CONTROL         "",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON | WS_GROUP,134,42,11,8
+    CONTROL         "",IDC_RADIO4,"Button",BS_AUTORADIOBUTTON,146,42,11,8
+    CONTROL         "",IDC_RADIO5,"Button",BS_AUTORADIOBUTTON,158,42,11,8
+    CONTROL         "",IDC_RADIO6,"Button",BS_AUTORADIOBUTTON,134,31,11,8
+    CONTROL         "",IDC_RADIO7,"Button",BS_AUTORADIOBUTTON,146,31,11,8
+    CONTROL         "",IDC_RADIO8,"Button",BS_AUTORADIOBUTTON,158,31,11,8
+    CONTROL         "",IDC_RADIO9,"Button",BS_AUTORADIOBUTTON,134,20,11,8
+    CONTROL         "",IDC_RADIO10,"Button",BS_AUTORADIOBUTTON,146,20,11,8
+    CONTROL         "",IDC_RADIO11,"Button",BS_AUTORADIOBUTTON,158,20,11,8
+    LTEXT           "Left",IDC_STATIC,176,22,14,8
+    EDITTEXT        IDC_EDIT7,198,19,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN6,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,227,19,11,14
+    LTEXT           "Right",IDC_STATIC,176,39,18,8
+    EDITTEXT        IDC_EDIT8,198,37,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN7,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,227,37,11,14
+    LTEXT           "Top",IDC_STATIC,240,22,13,8
+    EDITTEXT        IDC_EDIT9,268,19,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN8,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,296,17,11,14
+    LTEXT           "Bottom",IDC_STATIC,240,39,24,8
+    EDITTEXT        IDC_EDIT10,268,37,34,14,ES_RIGHT | ES_AUTOHSCROLL
+    CONTROL         "",IDC_SPIN9,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,296,37,11,14
+    GROUPBOX        "Colors && Transparency",IDC_STATIC,125,63,187,110
+    LTEXT           "Primary",IDC_STATIC,133,88,25,8
+    CONTROL         "",IDC_COLORPRI,"Static",SS_OWNERDRAW | SS_NOTIFY,175,85,16,14,WS_EX_DLGMODALFRAME
+    CONTROL         "",IDC_SLIDER2,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,198,86,103,13
+    LTEXT           "Secondary",IDC_STATIC,133,105,34,8
+    CONTROL         "",IDC_COLORSEC,"Static",SS_OWNERDRAW | SS_NOTIFY,175,102,16,14,WS_EX_DLGMODALFRAME
+    CONTROL         "",IDC_SLIDER3,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,198,103,103,13
+    LTEXT           "Outline",IDC_STATIC,133,122,24,8
+    CONTROL         "",IDC_COLOROUTL,"Static",SS_OWNERDRAW | SS_NOTIFY,175,119,16,14,WS_EX_DLGMODALFRAME
+    CONTROL         "",IDC_SLIDER5,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,198,120,103,13
+    LTEXT           "Shadow",IDC_STATIC,133,139,26,8
+    CONTROL         "",IDC_COLORSHAD,"Static",SS_OWNERDRAW | SS_NOTIFY,175,136,16,14,WS_EX_DLGMODALFRAME
+    CONTROL         "",IDC_SLIDER6,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,198,137,103,13
+    CONTROL         "Link alpha channels",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,213,154,76,10
+    PUSHBUTTON      "Cancel",IDCANCEL,211,197,50,14
+    DEFPUSHBUTTON   "OK",IDOK,265,197,50,14
+    LTEXT           "100%",IDC_STATIC,289,76,20,8
+    LTEXT           "0%",IDC_STATIC,200,76,12,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1               ICON                    "res\\Icon_147.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+    IDD_DVSABOUTPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 239
+        VERTGUIDE, 14
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 139
+    END
+
+    IDD_DVSCOLORPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 239
+        VERTGUIDE, 41
+        VERTGUIDE, 118
+        VERTGUIDE, 160
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 19
+        HORZGUIDE, 134
+        HORZGUIDE, 147
+        HORZGUIDE, 153
+        HORZGUIDE, 163
+    END
+
+    IDD_DVSGENERALPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 240
+        VERTGUIDE, 17
+        VERTGUIDE, 26
+        VERTGUIDE, 112
+        VERTGUIDE, 240
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 126
+        HORZGUIDE, 166
+    END
+
+    IDD_DVSMAINPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 239
+        VERTGUIDE, 15
+        VERTGUIDE, 27
+        VERTGUIDE, 110
+        VERTGUIDE, 124
+        VERTGUIDE, 132
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 219
+        HORZGUIDE, 24
+        HORZGUIDE, 188
+    END
+
+    IDD_DVSMISCPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 239
+        VERTGUIDE, 16
+        VERTGUIDE, 26
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 26
+    END
+
+    IDD_DVSPATHSPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 240
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+    END
+
+    IDD_DVSTIMINGPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 240
+        VERTGUIDE, 20
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 127
+    END
+
+    IDD_DVSZOOMPAGE, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 239
+        VERTGUIDE, 36
+        VERTGUIDE, 46
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 181
+        HORZGUIDE, 18
+        HORZGUIDE, 38
+        HORZGUIDE, 58
+        HORZGUIDE, 78
+    END
+
+    IDD_STYLEDIALOG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 316
+        VERTGUIDE, 136
+        VERTGUIDE, 186
+        VERTGUIDE, 201
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 212
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE 
+BEGIN
+    IDS_PROJNAME            "&DirectVobSub"
+    IDD_DVSPATHSPAGE        "&Paths"
+    IDS_PROJNAME2           "&Subtitle Source"
+    IDS_DIVX_WARNING        "The found DivX DirectShow decoder (divx_c32.ax) may be\nincompatible with the current operating system. Please, try\nto install version 3.11 or 4.0+.\n\n(This message will appear only once per installation)"
+    IDD_DVSABOUTPAGE        "&About"
+    IDS_R_TEXT              "Text"
+    IDS_R_VOBSUB            "VobSub"
+    IDS_R_DEFTEXTPATHES     "DefTextPathes"
+    IDS_R_PREFLANGS         "PreferedLanguages"
+    IDS_RG_SAVEFULLPATH     "SaveFullPath"
+    IDS_RG_EXTPIC           "Extpic"
+    IDS_RG_MOD32FIX         "Mod32Fix"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RG_LOADLEVEL        "LoadLevel"
+    IDS_RG_INSTANTUPDATE    "InstantUpdate"
+    IDS_RG_FLIPPICTURE      "FlipPicture"
+    IDS_RG_HIDE             "Hide"
+    IDS_RG_NOYUY2COLORS     "NoYUY2Colors"
+    IDS_RG_DOPREBUFFERING   "DoPreBuffering"
+    IDS_RG_SHOWOSDSTATS     "ShowOSDStats"
+    IDS_RG_SEENDIVXWARNING  "SeenDivxWarning"
+    IDS_RG_RESX2            "ResX2Mode"
+    IDS_RG_RESX2MINW        "ResX2MinWidth"
+    IDS_RG_RESX2MINH        "ResX2MinHeight"
+    IDS_RTM_MEDIAFPS        "MediaFPS"
+    IDS_RT_SHADOW           "Shadow"
+    IDS_RT_OUTLINE          "Outline"
+    IDS_RT_LOGFONT          "LOGFONT"
+    IDS_RT_COLOR            "Color"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RT_OVERRIDEPLACEMENT "OverridePlacement"
+    IDS_RT_XPERC            "XPerc"
+    IDS_RT_YPERC            "YPerc"
+    IDS_RV_BUFFER           "Buffer"
+    IDS_RP_PATH             "Path%d"
+    IDS_RL_LANG             "Lang%d"
+    IDS_M_SHOWSUBTITLES     "Show Subtitles"
+    IDS_M_HIDESUBTITLES     "Hide Subtitles"
+    IDS_M_ORIGINALPICTURE   "Original Picture"
+    IDS_M_FLIPPEDPICTURE    "Flipped Picture"
+    IDS_RV_POLYGONIZE       "Polygonize"
+    IDS_RG_EXTERNALLOAD     "ExtLoad"
+    IDS_RG_WEBLOAD          "WebLoad"
+    IDS_RG_EMBEDDEDLOAD     "EmbLoad"
+    IDS_URL_HOMEPAGE        "http://gabest.org/"
+    IDS_URL_EMAIL           "mailto:gabest@freemail.hu"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_R_TIMING            "Timing"
+    IDS_RTM_SUBTITLEDELAY   "Subtitle Delay"
+    IDS_RTM_SUBTITLESPEEDMUL "Subtitle Speedmul"
+    IDS_RT_ADVANCEDRENDERER "AdvancedRenderer"
+    IDS_RV_ONLYSHOWFORCEDSUBS "OnlyShowForcedSubs"
+    IDS_ORGHEIGHT           "Original height"
+    IDS_RG_VMRZOOMENABLED   "VMRZoomEnabled"
+    IDS_RTM_SUBTITLESPEEDDIV "Subtitle Speeddiv"
+    IDS_RG_COLORFORMATS     "Color Formats"
+    IDS_EXTTO169            "Extend to 16:9"
+    IDS_RG_FORCERGB         "ForceRGB"
+    IDS_RG_ENABLEZPICON     "EnableZPIcon"
+    IDS_RG_FLIPSUBTITLES    "FlipSubtitles"
+    IDS_RTM_MEDIAFPSENABLED "MediaFPSEnabled"
+    IDS_RG_DISABLERELOADER  "DisableReloader"
+    IDS_EXTTO43             "Extend to 4:3"
+END
+
+STRINGTABLE 
+BEGIN
+    IDD_DVSCOLORPAGE        "&Colors"
+    IDD_DVSGENERALPAGE      "&General"
+    IDD_DVSTIMINGPAGE       "&Timing"
+    IDD_DVSMISCPAGE         "M&isc"
+    IDS_R_GENERAL           "General"
+END
+
+STRINGTABLE 
+BEGIN
+    IDD_DVSMAINPAGE         "&Main"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_CROPTO169           "Crop to 16:9"
+    IDS_CROPTO43            "Crop to 4:3"
+    IDS_ORGRES              "Original resolution"
+    IDS_DBLRES              "Doubled resolution"
+    IDS_DBLRESIF            "Double if smaller than"
+    IDS_DONOTLOAD           "Do not load"
+    IDS_LOADWHENNEEDED      "Load when needed"
+    IDS_ALWAYSLOAD          "Always load"
+    IDS_EXTTO576            "Extend to 576 lines"
+    IDS_EXTTO480            "Extend to 480 lines"
+END
+
+STRINGTABLE 
+BEGIN
+    IDS_RT_STYLE            "Style"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/vsfilter/vsfilter.sln b/vsfilter/vsfilter.sln
new file mode 100644
index 0000000000000000000000000000000000000000..b648da5907e3cb7cfed5527ee127190c890026a3
--- /dev/null
+++ b/vsfilter/vsfilter.sln
@@ -0,0 +1,60 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vsfilter", "vsfilter.vcproj", "{7A3950FD-A235-459A-ACA2-5170863CD32C}"
+	ProjectSection(ProjectDependencies) = postProject
+		{11B20554-2747-469C-9093-3345B1D99E0C} = {11B20554-2747-469C-9093-3345B1D99E0C}
+		{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6} = {5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}
+		{6BB08A9D-BBD3-433A-A6D9-079C84EBE270} = {6BB08A9D-BBD3-433A-A6D9-079C84EBE270}
+		{B0CD35D2-65C8-48D0-BEC8-D235137F62F6} = {B0CD35D2-65C8-48D0-BEC8-D235137F62F6}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subtitles", "subtitles\subtitles.vcproj", "{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}"
+	ProjectSection(ProjectDependencies) = postProject
+		{11B20554-2747-469C-9093-3345B1D99E0C} = {11B20554-2747-469C-9093-3345B1D99E0C}
+		{971851AB-A612-4F2F-866F-F0FA152EFFB2} = {971851AB-A612-4F2F-866F-F0FA152EFFB2}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subpic", "subpic\subpic.vcproj", "{11B20554-2747-469C-9093-3345B1D99E0C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libssf", "libssf\libssf.vcproj", "{971851AB-A612-4F2F-866F-F0FA152EFFB2}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dsutil", "dsutil\dsutil.vcproj", "{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basevideofilter", "basevideofilter\basevideofilter.vcproj", "{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Win32 = Debug|Win32
+		Release|Win32 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{7A3950FD-A235-459A-ACA2-5170863CD32C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{7A3950FD-A235-459A-ACA2-5170863CD32C}.Debug|Win32.Build.0 = Debug|Win32
+		{7A3950FD-A235-459A-ACA2-5170863CD32C}.Release|Win32.ActiveCfg = Release|Win32
+		{7A3950FD-A235-459A-ACA2-5170863CD32C}.Release|Win32.Build.0 = Release|Win32
+		{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}.Debug|Win32.ActiveCfg = Debug|Win32
+		{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}.Debug|Win32.Build.0 = Debug|Win32
+		{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}.Release|Win32.ActiveCfg = Release|Win32
+		{6BB08A9D-BBD3-433A-A6D9-079C84EBE270}.Release|Win32.Build.0 = Release|Win32
+		{11B20554-2747-469C-9093-3345B1D99E0C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{11B20554-2747-469C-9093-3345B1D99E0C}.Debug|Win32.Build.0 = Debug|Win32
+		{11B20554-2747-469C-9093-3345B1D99E0C}.Release|Win32.ActiveCfg = Release|Win32
+		{11B20554-2747-469C-9093-3345B1D99E0C}.Release|Win32.Build.0 = Release|Win32
+		{971851AB-A612-4F2F-866F-F0FA152EFFB2}.Debug|Win32.ActiveCfg = Debug|Win32
+		{971851AB-A612-4F2F-866F-F0FA152EFFB2}.Debug|Win32.Build.0 = Debug|Win32
+		{971851AB-A612-4F2F-866F-F0FA152EFFB2}.Release|Win32.ActiveCfg = Release|Win32
+		{971851AB-A612-4F2F-866F-F0FA152EFFB2}.Release|Win32.Build.0 = Release|Win32
+		{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}.Debug|Win32.ActiveCfg = Debug|Win32
+		{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}.Debug|Win32.Build.0 = Debug|Win32
+		{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}.Release|Win32.ActiveCfg = Release|Win32
+		{5F6E4984-0E0A-468D-8EF5-AE52AE8CBBF6}.Release|Win32.Build.0 = Release|Win32
+		{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}.Debug|Win32.Build.0 = Debug|Win32
+		{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}.Release|Win32.ActiveCfg = Release|Win32
+		{B0CD35D2-65C8-48D0-BEC8-D235137F62F6}.Release|Win32.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/vsfilter/vsfilter.vcproj b/vsfilter/vsfilter.vcproj
new file mode 100644
index 0000000000000000000000000000000000000000..5239cdf02c2e419348dab3487af44be6a1300839
--- /dev/null
+++ b/vsfilter/vsfilter.vcproj
@@ -0,0 +1,313 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="vsfilter"
+	ProjectGUID="{7A3950FD-A235-459A-ACA2-5170863CD32C}"
+	RootNamespace="vsfilter"
+	Keyword="MFCDLLProj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="_DEBUG"
+				MkTypLibCompatible="false"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;_USRDLL;WINVER=0x0500;_WIN32_WINNT=0x501;ISOLATION_AWARE_ENABLED;"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_DEBUG"
+				Culture="1033"
+				AdditionalIncludeDirectories="$(IntDir)"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="delayimp.lib strmbasdu.lib"
+				LinkIncremental="2"
+				ModuleDefinitionFile=".\vsfilter.def"
+				GenerateDebugInformation="true"
+				SubSystem="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>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			UseOfMFC="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="NDEBUG"
+				MkTypLibCompatible="false"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;_USRDLL;WINVER=0x0500;_WIN32_WINNT=0x501;ISOLATION_AWARE_ENABLED;"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="NDEBUG"
+				Culture="1033"
+				AdditionalIncludeDirectories="$(IntDir)"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="delayimp.lib strmbaseu.lib"
+				LinkIncremental="1"
+				ModuleDefinitionFile=".\vsfilter.def"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				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=".\Copy.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\csriapi.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DirectVobSub.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DirectVobSubFilter.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\DirectVobSubPropPage.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\plugins.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\StdAfx.cpp"
+				>
+				<FileConfiguration
+					Name="Debug|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release|Win32"
+					>
+					<Tool
+						Name="VCCLCompilerTool"
+						UsePrecompiledHeader="1"
+					/>
+				</FileConfiguration>
+			</File>
+			<File
+				RelativePath=".\StyleEditorDialog.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\Systray.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\TextInputPin.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\VSFilter.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\DirectVobSub.h"
+				>
+			</File>
+			<File
+				RelativePath=".\DirectVobSubFilter.h"
+				>
+			</File>
+			<File
+				RelativePath=".\DirectVobSubPropPage.h"
+				>
+			</File>
+			<File
+				RelativePath=".\IDirectVobSub.h"
+				>
+			</File>
+			<File
+				RelativePath=".\resource.h"
+				>
+			</File>
+			<File
+				RelativePath=".\StdAfx.h"
+				>
+			</File>
+			<File
+				RelativePath=".\StyleEditorDialog.h"
+				>
+			</File>
+			<File
+				RelativePath=".\Systray.h"
+				>
+			</File>
+			<File
+				RelativePath=".\TextInputPin.h"
+				>
+			</File>
+			<File
+				RelativePath=".\VSFilter.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}"
+			>
+			<File
+				RelativePath=".\vsfilter.rc"
+				>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>