Index: /dsfilters/demux_mpc/demux_mpc.rc
===================================================================
--- /dsfilters/demux_mpc/demux_mpc.rc	(revision 420)
+++ /dsfilters/demux_mpc/demux_mpc.rc	(revision 421)
@@ -61,6 +61,6 @@
 
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 0,3,1,2
- PRODUCTVERSION 0,3,1,2
+ FILEVERSION 0,4,0,0
+ PRODUCTVERSION 0,4,0,0
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
@@ -79,10 +79,10 @@
             VALUE "Comments", "Musepack Splitter"
             VALUE "FileDescription", "mmmpcdmx"
-            VALUE "FileVersion", "0, 3, 1, 2"
+            VALUE "FileVersion", "0, 4, 0, 0"
             VALUE "InternalName", "MONOGRAM Musepack Splitter"
             VALUE "LegalCopyright", "Copyright (C) 2007"
             VALUE "OriginalFilename", "mmmpcdmx.ax"
             VALUE "ProductName", "MONOGRAM Musepack Splitter"
-            VALUE "ProductVersion", "0, 3, 1, 2"
+            VALUE "ProductVersion", "0, 4, 0, 0"
         END
     END
@@ -122,16 +122,16 @@
 //
 
-IDD_PAGE_DEMUX DIALOGEX 0, 0, 280, 152
+IDD_PAGE_DEMUX DIALOGEX 0, 0, 280, 182
 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
 FONT 8, "MS Shell Dlg", 0, 0, 0x0
 BEGIN
-    CTEXT           "This software is released under the GNU LGPL license.",IDC_STATIC,162,72,108,18
-    CTEXT           "http://www.musepack.net",IDC_STATIC,162,132,108,8
-    CTEXT           "DirectShow filter implemented by Igor Janos",IDC_STATIC,162,96,108,18
-    CTEXT           "http://blog.monogram.sk/janos",IDC_STATIC,162,120,108,8
-    CONTROL         "",IDC_LIST_CONTENT,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,6,36,144,108,WS_EX_CLIENTEDGE
+    CTEXT           "This software is released under the GNU LGPL license.",IDC_STATIC,162,84,108,18
+    CTEXT           "http://www.musepack.net",IDC_STATIC,162,162,108,8
+    CTEXT           "DirectShow filter implemented by Igor Janos",IDC_STATIC,162,108,108,18
+    CTEXT           "http://blog.monogram.sk/janos",IDC_STATIC,162,150,108,8
+    CONTROL         "",IDC_LIST_CONTENT,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_TABSTOP,6,36,144,138,WS_EX_CLIENTEDGE
     CONTROL         101,IDC_STATIC,"Static",SS_BITMAP,0,0,280,30
     CTEXT           "MONOGRAM Musepack Splitter",IDC_STATIC,162,36,108,8
-    CTEXT           "0.3.1.2 beta",IDC_STATIC,162,48,108,8
+    CTEXT           "0.4.0.0",IDC_STATIC,162,48,108,8
 END
 
@@ -150,5 +150,5 @@
         RIGHTMARGIN, 273
         TOPMARGIN, 7
-        BOTTOMMARGIN, 145
+        BOTTOMMARGIN, 175
     END
 END
Index: /dsfilters/demux_mpc/demux_mpc.vcproj
===================================================================
--- /dsfilters/demux_mpc/demux_mpc.vcproj	(revision 420)
+++ /dsfilters/demux_mpc/demux_mpc.vcproj	(revision 421)
@@ -350,5 +350,13 @@
 			>
 			<File
+				RelativePath=".\src\ape_tag.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\src\bits.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\src\media_content.cpp"
 				>
 			</File>
@@ -380,5 +388,13 @@
 			>
 			<File
+				RelativePath=".\src\ape_tag.h"
+				>
+			</File>
+			<File
 				RelativePath=".\src\bits.h"
+				>
+			</File>
+			<File
+				RelativePath=".\src\media_content.h"
 				>
 			</File>
Index: /dsfilters/demux_mpc/src/ape_tag.cpp
===================================================================
--- /dsfilters/demux_mpc/src/ape_tag.cpp	(revision 421)
+++ /dsfilters/demux_mpc/src/ape_tag.cpp	(revision 421)
@@ -0,0 +1,266 @@
+//-----------------------------------------------------------------------------
+//
+//	Musepack Demuxer
+//
+//	Author : Igor Janos
+//
+//-----------------------------------------------------------------------------
+#include "stdafx.h"
+
+
+//-----------------------------------------------------------------------------
+//
+//	APE_Tag_Item class
+//
+//-----------------------------------------------------------------------------
+
+APE_Tag_Item::APE_Tag_Item()
+{
+	name	= _T("");
+	value	= _T("");
+	type	= APE_Tag_Item::TYPE_STRING;
+	buf		= NULL;
+	len		= 0;
+}
+
+APE_Tag_Item::~APE_Tag_Item()
+{
+	if (buf) {
+		free(buf);
+		buf = NULL;
+	}
+}
+
+int APE_Tag_Item::Load(BYTE *buf, BYTE *end)
+{
+	// there must be at least 8 bytes to read the size and flags
+	int	left = end - buf;
+	if (left < 8) return -1;
+
+	BYTE	*orig	= buf;		// remember the starting position
+	DWORD	size	= (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	flags	= (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+
+	// key is ASCII
+	int		keylen = 0;
+	char	*start = (char*)buf;
+	// make sure we access only those bytes we can
+	while (buf < end) {
+		if (buf[0] == 0) { break; } else {
+			keylen ++;
+			buf++;
+		}
+	}
+
+	CStringA	akey;
+	char		*dst = akey.GetBufferSetLength(keylen);
+	if (!dst) return -1;		// not enough memory
+	memcpy(dst, start, keylen);
+	name = akey;
+
+	// skip the '0' terminator
+	buf ++;
+
+	// and make sure there is enough bytes for us to read
+	if (buf + size > end) return -1;
+
+	int		t = (flags >> 1) & 0x03;		// 2..1 bits
+	switch (t) {
+	case 0:		type = APE_Tag_Item::TYPE_STRING; break;
+	case 1:		type = APE_Tag_Item::TYPE_BINARY; break;
+	case 2:		type = APE_Tag_Item::TYPE_LOCATOR; break;
+	default:
+		{
+			// reserved
+			return -1;
+		}
+		break;
+	}
+
+	if (type == APE_Tag_Item::TYPE_BINARY) {
+		// read the binary buffer
+		this->buf = (BYTE*)malloc(size);
+		if (!this->buf) return -1;			// out of memory
+
+		// read the bytes
+		memcpy(this->buf, buf, size);
+		len = size;
+		buf += size;
+
+	} else {
+		// read the UTF-8 string
+		int			cnt = size*2;
+		WCHAR		*wstr = (WCHAR*)malloc(cnt * sizeof(WCHAR));
+		if (!wstr) return -1;
+		memset(wstr, 0, cnt*sizeof(WCHAR));
+		
+		// convert the string
+		MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)buf, size, wstr, cnt);	
+
+		// and save the value
+		value = CString(wstr);
+
+		free(wstr);
+
+		buf += size;
+	}
+
+	// returns the number of bytes eaten
+	return (buf - orig);
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//	APE_Tag class
+//
+//-----------------------------------------------------------------------------
+
+APE_Tag::APE_Tag()
+{
+}
+
+APE_Tag::~APE_Tag()
+{
+	Clear();
+}
+
+void APE_Tag::Clear()
+{
+	for (int i=0; i<items.size(); i++) {
+		APE_Tag_Item	*item = items[i];
+		delete item;
+	}
+	items.clear();
+}
+
+CString APE_Tag::FindName(CString name)
+{
+	// simplified.. just strings
+	APE_Tag_Item	*tag = Find(name);
+	if (!tag) return _T("");
+
+	if (tag->type == APE_Tag_Item::TYPE_BINARY) return _T("");
+	return tag->value;
+}
+
+APE_Tag_Item *APE_Tag::Find(CString name)
+{
+	CString		name_lc = name;
+	name_lc.MakeLower();
+
+	for (int i=0; i<items.size(); i++) {
+		APE_Tag_Item	*item	= items[i];
+		CString			in		= item->name;
+		in.MakeLower();
+
+		// compare strings in lower case
+		if (in == name_lc) {
+			return item;
+		}
+	}
+
+	return NULL;
+}
+
+int APE_Tag::ReadFooter(BYTE *buf, int len, int *tagsize)
+{
+	/*
+		Footer must be exactly 32 bytes long.
+	*/
+	if (len < 32) return -1;
+
+	// check the preamble
+	BYTE		preamble[8] = { 'A','P','E','T','A','G','E','X' };
+	BYTE		reserved[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+	if (memcmp(buf, preamble, 8) != 0) return -1;
+	buf += 8;
+
+	DWORD	ver	 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	cnt  = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	flag = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+
+	if (ver != 2000) return -1;			// not an APE Tag v2.0
+
+	// check the flags
+	bool		hdr = (flag >> 31) & 0x01;		if (!hdr) return -1;
+	int			ft	= (flag >> 29) & 0x01;		if (ft) return -1;		// not a footer
+
+	// 8 zero bytes
+	if (memcmp(buf, reserved, 8) != 0) return -1;
+	
+	// return the size of APE Tag and finish
+	if (tagsize) *tagsize = (int)size;
+	return 0;
+}
+
+int APE_Tag::ReadTag(BYTE *buf, int len, bool skip_preamble)
+{
+	int			req_len = 32;
+	if (skip_preamble) req_len -= 8;
+	if (len < req_len) return -1;
+
+	// end of our buffer
+	BYTE		*end = buf + len;
+	BYTE		preamble[8] = { 'A','P','E','T','A','G','E','X' };
+	BYTE		reserved[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	// check the preamble
+	if (!skip_preamble) {
+		if (memcmp(buf, preamble, 8) != 0) return -1;
+		buf += 8;
+		len -= 8;
+	}
+
+	DWORD	ver	 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	size = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	cnt  = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	DWORD	flag = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0]);	buf += 4;
+	len -= 16;
+
+	if (ver != 2000) return -1;			// not an APE Tag v2.0
+
+	// check the flags
+	bool		hdr = (flag >> 31) & 0x01;		if (!hdr) return -1;
+	int			hd	= (flag >> 29) & 0x01;		if (!hd) return -1;		// not a header
+
+	// 8 zero bytes
+	if (memcmp(buf, reserved, 8) != 0) return -1;
+
+	// start of the items
+	buf += 8;
+	len -= 8;
+
+	// do we have enough data ?
+	if (len < size) return -1;
+
+	// now read all the tag items
+	for (int i=0; i<cnt; i++) {
+		APE_Tag_Item	*item = new APE_Tag_Item();
+		int				ret = item->Load(buf, end);
+
+		if (ret <= 0) {
+			// cannot load item - abort loading
+			delete item;
+			break;
+		} else {
+
+			// keep the item
+			items.push_back(item);
+
+			// and continue
+			buf += ret;
+		}
+	}
+
+	return 0;
+}
+
+
+
+
+
+
+
+
Index: /dsfilters/demux_mpc/src/ape_tag.h
===================================================================
--- /dsfilters/demux_mpc/src/ape_tag.h	(revision 421)
+++ /dsfilters/demux_mpc/src/ape_tag.h	(revision 421)
@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------------
+//
+//	Musepack Demuxer
+//
+//	Author : Igor Janos
+//
+//-----------------------------------------------------------------------------
+#pragma once
+
+
+//-----------------------------------------------------------------------------
+//
+//	APE_Tag_Item class
+//
+//-----------------------------------------------------------------------------
+
+class APE_Tag_Item
+{
+public:
+
+	CString			name;
+	CString			value;		
+
+	// in case of binary value
+	BYTE			*buf;
+	int				len;
+
+	enum {
+		TYPE_STRING		= 0,
+		TYPE_BINARY		= 1,
+		TYPE_LOCATOR	= 2	
+	};
+	int				type;
+
+public:
+	APE_Tag_Item();
+	virtual ~APE_Tag_Item();
+
+	// load
+	int Load(BYTE *buf, BYTE *end);
+
+};
+
+
+
+//-----------------------------------------------------------------------------
+//
+//	APE_Tag class
+//
+//-----------------------------------------------------------------------------
+
+class APE_Tag
+{
+public:
+
+	vector<APE_Tag_Item*>		items;
+
+public:
+	APE_Tag();
+	virtual ~APE_Tag();
+
+	void Clear();
+
+	// tag reading
+	int ReadFooter(BYTE *buf, int len, int *tagsize);
+	int ReadTag(BYTE *buf, int len, bool skip_preamble = false);
+
+	// find by name
+	APE_Tag_Item *Find(CString name);
+	CString FindName(CString name);
+};
+
Index: /dsfilters/demux_mpc/src/media_content.cpp
===================================================================
--- /dsfilters/demux_mpc/src/media_content.cpp	(revision 421)
+++ /dsfilters/demux_mpc/src/media_content.cpp	(revision 421)
@@ -0,0 +1,187 @@
+//-----------------------------------------------------------------------------
+//
+//	Musepack Demuxer
+//
+//	Author : Igor Janos
+//
+//-----------------------------------------------------------------------------
+#include "stdafx.h"
+
+
+
+//-----------------------------------------------------------------------------
+//
+//	MediaContentSource class
+//
+//-----------------------------------------------------------------------------
+
+MediaContentSource::MediaContentSource()
+{
+}
+
+MediaContentSource::~MediaContentSource()
+{
+}
+
+int MediaContentSource::GetContentString(CString key, CString &value)
+{
+	// not implemented yet
+	value = _T("");
+	return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//	CAMMediaContent class
+//
+//-----------------------------------------------------------------------------
+
+CAMMediaContent::CAMMediaContent(MediaContentSource *src, LPUNKNOWN pUnk) :
+    CUnknown(_T("MediaContent"), pUnk),
+	source(src)
+{
+}
+
+STDMETHODIMP CAMMediaContent::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
+{
+    ValidateReadWritePtr(ppv,sizeof(PVOID));
+    if (riid == IID_IAMMediaContent) {
+		return GetInterface((IAMMediaContent*)this, ppv);
+    } else {
+		return CUnknown::NonDelegatingQueryInterface(riid, ppv);
+    }
+}
+
+
+// return 1 if we support GetTypeInfo
+STDMETHODIMP CAMMediaContent::GetTypeInfoCount(__out UINT * pctinfo)
+{
+    return basedisp.GetTypeInfoCount(pctinfo);
+}
+
+// attempt to find our type library
+STDMETHODIMP CAMMediaContent::GetTypeInfo(UINT itinfo, LCID lcid, __deref_out ITypeInfo ** pptinfo)
+{
+    return basedisp.GetTypeInfo(IID_IAMMediaContent, itinfo, lcid, pptinfo);
+}
+
+STDMETHODIMP CAMMediaContent::GetIDsOfNames(REFIID riid,
+  __in_ecount(cNames) LPOLESTR * rgszNames, UINT cNames,
+  LCID lcid, __out_ecount(cNames) DISPID * rgdispid)
+{
+    return basedisp.GetIDsOfNames(IID_IAMMediaContent, rgszNames, cNames, lcid, rgdispid);
+}
+
+STDMETHODIMP CAMMediaContent::Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
+  WORD wFlags, __in DISPPARAMS * pdispparams, __out_opt VARIANT * pvarResult,
+  __out_opt EXCEPINFO * pexcepinfo, __out_opt UINT * puArgErr)
+{
+    // this parameter is a dead leftover from an earlier interface
+    if (IID_NULL != riid) {	return DISP_E_UNKNOWNINTERFACE; }
+
+    ITypeInfo * pti;
+    HRESULT hr = GetTypeInfo(0, lcid, &pti);
+
+    if (FAILED(hr)) return hr;
+
+    hr = pti->Invoke(
+	    (IAMMediaContent *)this,
+	    dispidMember,
+	    wFlags,
+	    pdispparams,
+	    pvarResult,
+	    pexcepinfo,
+	    puArgErr);
+
+    pti->Release();
+    return hr;
+}
+
+HRESULT CAMMediaContent::LoadContent(CString name, BSTR FAR *value)
+{
+	if (!value) return E_POINTER;
+
+	CString		val;
+	int			ret = -1;
+
+	if (source) {
+		ret = source->GetContentString(name, val);
+	}
+
+	if (ret < 0) {
+		return E_FAIL;
+	} 
+
+	// alloc the value 
+	*value = SysAllocString((const OLECHAR*)val.GetBuffer());
+	return NOERROR;
+}
+
+STDMETHODIMP CAMMediaContent::get_AuthorName(BSTR FAR* pbstrAuthorName)
+{
+	return LoadContent(_T("author"), pbstrAuthorName);
+}
+
+STDMETHODIMP CAMMediaContent::get_Title(BSTR FAR* pbstrTitle)
+{
+	return LoadContent(_T("title"), pbstrTitle);
+}
+
+STDMETHODIMP CAMMediaContent::get_Rating(BSTR FAR* pbstrRating)
+{
+	return LoadContent(_T("rating"), pbstrRating);
+}
+
+STDMETHODIMP CAMMediaContent::get_Description(BSTR FAR* pbstrDescription)
+{
+	return LoadContent(_T("description"), pbstrDescription);
+}
+
+STDMETHODIMP CAMMediaContent::get_Copyright(BSTR FAR* pbstrCopyright)
+{
+	return LoadContent(_T("copyright"), pbstrCopyright);
+}
+
+STDMETHODIMP CAMMediaContent::get_BaseURL(BSTR FAR* pbstrBaseURL)
+{
+	return LoadContent(_T("base_url"), pbstrBaseURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_LogoURL(BSTR FAR* pbstrLogoURL)
+{
+	return LoadContent(_T("logo_url"), pbstrLogoURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_LogoIconURL(BSTR FAR* pbstrLogoURL)
+{
+	return LoadContent(_T("logo_icon_url"), pbstrLogoURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_WatermarkURL(BSTR FAR* pbstrWatermarkURL)
+{
+	return LoadContent(_T("watermark_url"), pbstrWatermarkURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_MoreInfoURL(BSTR FAR* pbstrMoreInfoURL)
+{
+	return LoadContent(_T("more_info_url"), pbstrMoreInfoURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_MoreInfoBannerImage(BSTR FAR* pbstrMoreInfoBannerImage)
+{
+	return LoadContent(_T("more_info_banner_image"), pbstrMoreInfoBannerImage);
+}
+
+STDMETHODIMP CAMMediaContent::get_MoreInfoBannerURL(BSTR FAR* pbstrMoreInfoBannerURL)
+{
+	return LoadContent(_T("more_info_banner_url"), pbstrMoreInfoBannerURL);
+}
+
+STDMETHODIMP CAMMediaContent::get_MoreInfoText(BSTR FAR* pbstrMoreInfoText)
+{
+	return LoadContent(_T("more_info_text"), pbstrMoreInfoText);
+}
+
+
+
Index: /dsfilters/demux_mpc/src/media_content.h
===================================================================
--- /dsfilters/demux_mpc/src/media_content.h	(revision 421)
+++ /dsfilters/demux_mpc/src/media_content.h	(revision 421)
@@ -0,0 +1,88 @@
+//-----------------------------------------------------------------------------
+//
+//	Musepack Demuxer
+//
+//	Author : Igor Janos
+//
+//-----------------------------------------------------------------------------
+#pragma once
+
+
+//-----------------------------------------------------------------------------
+//
+//	MediaContentSource class
+//
+//-----------------------------------------------------------------------------
+
+class MediaContentSource
+{
+public:
+	MediaContentSource();
+	virtual ~MediaContentSource();
+
+	// to be overriden
+	virtual int GetContentString(CString key, CString &value);
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//	CAMMediaContent class
+//
+//-----------------------------------------------------------------------------
+
+class CAMMediaContent :
+    public CUnknown,
+    public IAMMediaContent
+{
+    CBaseDispatch			basedisp;
+	MediaContentSource		*source;
+
+public:
+    CAMMediaContent(MediaContentSource *src, LPUNKNOWN pUnk);
+
+    DECLARE_IUNKNOWN
+
+    // override this to publicise our interfaces
+    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv);
+
+    /* IDispatch methods */
+    STDMETHODIMP GetTypeInfoCount(__out UINT * pctinfo);
+    STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, __deref_out ITypeInfo ** pptinfo);
+    STDMETHODIMP GetIDsOfNames(
+			REFIID riid,
+			__in_ecount(cNames) LPOLESTR * rgszNames,
+			UINT cNames,
+			LCID lcid,
+			__out_ecount(cNames) DISPID * rgdispid
+			);
+
+    STDMETHODIMP Invoke(
+			DISPID dispidMember,
+			REFIID riid,
+			LCID lcid,
+			WORD wFlags,
+			__in DISPPARAMS * pdispparams,
+			__out_opt VARIANT * pvarResult,
+			__out_opt EXCEPINFO * pexcepinfo,
+			__out_opt UINT * puArgErr
+			);
+
+    /* IAMMediaContent methods */
+    STDMETHODIMP get_AuthorName(BSTR FAR* pbstrAuthorName);
+    STDMETHODIMP get_Title(BSTR FAR* pbstrTitle);
+    STDMETHODIMP get_Rating(BSTR FAR* pbstrRating);
+    STDMETHODIMP get_Description(BSTR FAR* pbstrDescription);
+    STDMETHODIMP get_Copyright(BSTR FAR* pbstrCopyright);
+    STDMETHODIMP get_BaseURL(BSTR FAR* pbstrBaseURL);
+    STDMETHODIMP get_LogoURL(BSTR FAR* pbstrLogoURL);
+    STDMETHODIMP get_LogoIconURL(BSTR FAR* pbstrLogoURL);
+    STDMETHODIMP get_WatermarkURL(BSTR FAR* pbstrWatermarkURL);
+    STDMETHODIMP get_MoreInfoURL(BSTR FAR* pbstrMoreInfoURL);
+    STDMETHODIMP get_MoreInfoBannerImage(BSTR FAR* pbstrMoreInfoBannerImage);
+    STDMETHODIMP get_MoreInfoBannerURL(BSTR FAR* pbstrMoreInfoBannerURL);
+    STDMETHODIMP get_MoreInfoText(BSTR FAR* pbstrMoreInfoText);
+
+	// loadovanie
+	HRESULT LoadContent(CString name, BSTR FAR *value);
+};
Index: /dsfilters/demux_mpc/src/mpc_file.cpp
===================================================================
--- /dsfilters/demux_mpc/src/mpc_file.cpp	(revision 420)
+++ /dsfilters/demux_mpc/src/mpc_file.cpp	(revision 421)
@@ -37,6 +37,16 @@
 CMPCFile::~CMPCFile()
 {
+	ClearChapters();
 	if (seek_table) { free(seek_table); seek_table = NULL; }
 	if (extradata) { free(extradata); extradata = NULL; }
+}
+
+void CMPCFile::ClearChapters()
+{
+	for (int i=0; i<chapters.size(); i++) {
+		CMPCChapter	*chap = chapters[i];
+		delete chap;
+	}
+	chapters.clear();
 }
 
@@ -106,4 +116,25 @@
 		if (ret < 0) return ret;
 
+		// if there is a sequence of chapters it MUST follow the seeking table
+		while (true) {
+			ret = packet.Load(reader);
+			if (ret < 0) break;
+
+			if (packet.key == MPC_KEY('C','T')) {
+				CMPCChapter		*chap = new CMPCChapter();
+				ret = chap->Load(&packet);
+				if (ret < 0) {
+					delete chap;
+				} else {					
+					if (chap->caption == _T("")) {
+						chap->caption.Format(_T("Chapter %d"), (int)chapters.size() + 1);
+					}
+					chapters.push_back(chap);
+				}
+			} else {
+				break;
+			}
+		}
+
 		// seek back to first AP
 		reader->Seek(first_ap_pos);
@@ -256,4 +287,49 @@
 	HRESULT		hr;
 	int			ret;
+
+	//-------------------------------------------------------------------------
+	//
+	//	Load APE Tag
+	//
+	//-------------------------------------------------------------------------
+
+	// try to load the APE Tag
+	__int64		size, avail;
+	int			tagsize = 0;
+	reader->GetSize(&avail, &size);
+
+	tag.Clear();
+	if (size >= 32) {
+		BYTE		temp[32];
+
+		reader->Seek(size - 32);
+		reader->Read(temp, 32);
+
+		ret = tag.ReadFooter(temp, 32, &tagsize);
+		if (ret == 0 && tagsize > 0) {
+			// seek this many bytes back
+			reader->Seek(size - 32 - tagsize);
+			
+			// also load the footer
+			tagsize += 32;
+			BYTE		*apetag = (BYTE*)malloc(tagsize);
+			if (apetag) {
+				reader->Read(apetag, tagsize);
+
+				ret = tag.ReadTag(apetag, tagsize);
+				if (ret < 0) {
+					tag.Clear();
+				}
+
+				free(apetag);
+			}
+		}
+	}
+
+	//-------------------------------------------------------------------------
+	//
+	//	Load MPC file
+	//
+	//-------------------------------------------------------------------------
 
 	// keep a local copy of the reader
@@ -662,3 +738,61 @@
 }
 
-
+//-----------------------------------------------------------------------------
+//
+//	CMPCChapter
+//
+//-----------------------------------------------------------------------------
+
+CMPCChapter::CMPCChapter()
+{
+	sample_offset		= 0;
+	chapter_gain_db		= 0;
+	chapter_peak_db		= 0;
+}
+
+CMPCChapter::~CMPCChapter()
+{
+}
+
+int CMPCChapter::Load(CMPCPacket *packet)
+{
+	Bitstream	b(packet->payload);
+	b.NeedBits();
+
+	sample_offset = b.GetMpcSize();
+
+	int16	val;
+	b.NeedBits();	val = b.SGetBits(16);	chapter_gain_db = val / 256.0;
+	b.NeedBits();	val = b.UGetBits(16);	chapter_peak_db = val;
+
+	if (chapter_gain_db != 0) chapter_gain_db = OLD_GAIN_REF - chapter_gain_db;
+	if (chapter_peak_db != 0) {
+		chapter_peak_db = pow((double)10.0, (double)(chapter_peak_db / (20*256))) / (double)(1 << 15);
+	}
+
+	BYTE		*buf = b.Position();
+	BYTE		*end = packet->payload + packet->payload_size;
+
+	// let's try to load the APE Tag
+	APE_Tag		tag;
+	int ret = tag.ReadTag(buf, end-buf, true);
+	if (ret == 0) {
+
+		CString		title	= tag.FindName(_T("title"));
+		CString		track	= tag.FindName(_T("track"));
+		
+		if (track != _T("")) {
+			caption.Format(_T("%s [%s]"), title.GetBuffer(), track.GetBuffer());
+		} else {
+			caption = title;
+		}
+		caption.Trim();
+	
+	} else {
+		caption = _T("");
+	}
+
+	return 0;
+}
+
+
Index: /dsfilters/demux_mpc/src/mpc_file.h
===================================================================
--- /dsfilters/demux_mpc/src/mpc_file.h	(revision 420)
+++ /dsfilters/demux_mpc/src/mpc_file.h	(revision 421)
@@ -40,4 +40,26 @@
 };
 
+//-----------------------------------------------------------------------------
+//
+//	CMPCChapter
+//
+//-----------------------------------------------------------------------------
+
+class CMPCChapter
+{
+public:
+
+	int64			sample_offset;
+	float			chapter_gain_db;
+	float			chapter_peak_db;
+
+	CString			caption;
+
+public:
+	CMPCChapter();
+	virtual ~CMPCChapter();
+
+	int Load(CMPCPacket *packet);
+};
 
 //-----------------------------------------------------------------------------
@@ -64,28 +86,35 @@
 
 	// replay gain
-	float			gain_title_db;
-	float			gain_title_peak_db;
-	float			gain_album_db;
-	float			gain_album_peak_db;
+	float					gain_title_db;
+	float					gain_title_peak_db;
+	float					gain_album_db;
+	float					gain_album_peak_db;
 
 	// seeking table
-	int64			seek_table_position;		// position of seeking table in file (in bits)
-	int64			header_position;			// (in bits)
-	uint64			*seek_table;
-	int64			seek_table_size;
+	int64					seek_table_position;		// position of seeking table in file (in bits)
+	int64					header_position;			// (in bits)
+	uint64					*seek_table;
+	int64					seek_table_size;
 
 	// current position
-	int64			total_samples;
-	int64			current_sample;
+	int64					total_samples;
+	int64					current_sample;
 
 	// internals
-	CMPCReader		*reader;				// file reader interface
-	int				bits_to_skip;			// after seeking
+	CMPCReader				*reader;				// file reader interface
+	int						bits_to_skip;			// after seeking
 
 	// buffer for decoder specific info
-	uint8			*extradata;
-	int				extradata_max_size;		// total size
-	int				extradata_size;			// current size of extradata
+	uint8					*extradata;
+	int						extradata_max_size;		// total size
+	int						extradata_size;			// current size of extradata
 	
+	// APE Tags
+	APE_Tag					tag;
+
+	// chapters
+	vector<CMPCChapter*>	chapters;
+
+
 	int Open_SV8();
 	int Open_SV7();
@@ -96,4 +125,5 @@
 	// I/O for MPC file
 	int Open(CMPCReader *reader);
+	void ClearChapters();
 
 	// parsing packets
Index: /dsfilters/demux_mpc/src/mpc_filter.cpp
===================================================================
--- /dsfilters/demux_mpc/src/mpc_filter.cpp	(revision 420)
+++ /dsfilters/demux_mpc/src/mpc_filter.cpp	(revision 421)
@@ -54,6 +54,15 @@
 	rtStop(0xFFFFFFFFFFFFFF),
 	rate(1.0),
-	ev_abort(TRUE)
-{
+	ev_abort(TRUE),
+	content(NULL)
+{
+	// new instance of media content
+	CAMMediaContent	*amc = new CAMMediaContent(this, NULL);
+	HRESULT hr = amc->NonDelegatingQueryInterface(IID_IAMMediaContent, (void**)&content);
+	if (FAILED(hr)) {
+		delete amc;
+		content = NULL;
+	}
+
 	input = new CMPCInputPin(NAME("MPC Input Pin"), this, phr, L"In");
 	output.RemoveAll();
@@ -78,4 +87,7 @@
 	retired.RemoveAll();
 	if (input) { delete input; input = NULL; }
+
+	// done with ...
+	if (content) content->Release();
 }
 
@@ -92,4 +104,14 @@
     if (riid == IID_IMediaSeeking) {
         return GetInterface((IMediaSeeking*)this, ppv);
+	} else 
+    if (riid == IID_IAMStreamSelect) {
+        return GetInterface((IAMStreamSelect*)this, ppv);
+	} else 
+	if (riid == IID_IAMMediaContent) {
+		if (content) {
+			return content->QueryInterface(riid, ppv);
+		} else {
+			return E_NOINTERFACE;
+		}
 	} else {
 		return CBaseFilter::NonDelegatingQueryInterface(riid,ppv);
@@ -178,4 +200,6 @@
 		//
 		//---------------------------------------------------------------------
+		CAutoLock		lck(&lock_filter);
+
 		reader = new CMPCReader(input->Reader());
 		file = new CMPCFile();
@@ -610,2 +634,120 @@
 }
 
+int CMPCDemux::GetContentString(CString key, CString &value)
+{
+	CAutoLock		lck(&lock_filter);
+
+	if (!file) return -1;
+	
+	if (key == _T("author")) {	
+		value = file->tag.FindName(_T("artist")); 
+	} else
+	if (key == _T("title"))	{	
+		value = file->tag.FindName(_T("title"));
+	} else
+	if (key == _T("description")) {	
+		value = file->tag.FindName(_T("album"));
+		CString	year = file->tag.FindName(_T("year"));
+		if (year != _T("")) {
+			CString		tmp;
+			tmp.Format(_T("(%s) %s"), year, value);
+			value = tmp;
+		}
+	}
+	return (value == _T("") ? -1 : 0);
+}
+
+// IAMStreamSelect (chapters hack)
+STDMETHODIMP CMPCDemux::Count(DWORD *pcStreams)
+{
+	if (!pcStreams) return E_POINTER;
+
+	CAutoLock		lck(&lock_filter);
+	if (!file) {
+		*pcStreams = 0;
+		return NOERROR;
+	}
+
+	// pocet chaptersov
+	*pcStreams = file->chapters.size();
+	return 0;
+}
+
+STDMETHODIMP CMPCDemux::Info(long lIndex, AM_MEDIA_TYPE **ppmt, DWORD *pdwFlags,
+					  LCID *plcid, DWORD *pdwGroup, LPWSTR *ppszName, 
+					  IUnknown **ppObject, IUnknown **ppUnk)
+{
+	CAutoLock		lck(&lock_filter);
+
+	// correct range ?
+	if (lIndex < 0) return E_INVALIDARG;
+	if (!file) return S_FALSE;
+	if (lIndex >= file->chapters.size()) return S_FALSE;
+
+	CMPCChapter		*chap = file->chapters[lIndex];
+	if (!chap) return S_FALSE;
+
+	// all chapters are in the same group
+	if (pdwFlags) pdwFlags = 0; //AMSTREAMSELECTINFO_EXCLUSIVE;
+	if (plcid) *plcid = 0;
+	if (pdwGroup) *pdwGroup = 0;
+
+	if (ppszName) {
+		int		len = chap->caption.GetLength() * sizeof(WCHAR);
+
+		LPWSTR		str = (LPWSTR)CoTaskMemAlloc(len+ sizeof(WCHAR));
+		if (!str) return E_OUTOFMEMORY;
+
+		// copy the string
+		memset(str, 0, len+sizeof(WCHAR));
+		memcpy(str, chap->caption.GetBuffer(), len);
+		*ppszName = str;
+	}
+
+	// ignore these
+	if (ppmt) *ppmt = NULL;
+	if (ppObject) *ppObject = NULL;
+	if (ppUnk) *ppUnk = NULL;
+
+	return NOERROR;
+}
+
+STDMETHODIMP CMPCDemux::Enable(long lIndex, DWORD dwFlags)
+{
+	/*
+		we fake the stream selection by seeking to the desired position.
+	*/
+
+	REFERENCE_TIME				seek_time = 0;
+	CComPtr<IMediaSeeking>		ms;
+
+	{
+		CAutoLock		lck(&lock_filter);
+
+		if (!m_pGraph) return NOERROR;
+
+		HRESULT hr = m_pGraph->QueryInterface(IID_IMediaSeeking, (void**)&ms);
+		if (FAILED(hr)) return E_UNEXPECTED;
+
+		// correct range ?
+		if (lIndex < 0) return E_INVALIDARG;
+		if (!file) return S_FALSE;
+		if (lIndex >= file->chapters.size()) return S_FALSE;
+
+		CMPCChapter		*chap = file->chapters[lIndex];
+		if (!chap) return S_FALSE;
+
+		// this is the time
+		seek_time = llMulDiv(chap->sample_offset, 10000000, file->sample_rate, 0);
+	}
+
+	// now let's do the seeking
+	ASSERT(ms);
+
+	// make the seek
+	ms->SetPositions(&seek_time, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
+	ms = NULL;
+
+	return NOERROR;
+}
+
Index: /dsfilters/demux_mpc/src/mpc_filter.h
===================================================================
--- /dsfilters/demux_mpc/src/mpc_filter.h	(revision 420)
+++ /dsfilters/demux_mpc/src/mpc_filter.h	(revision 421)
@@ -21,7 +21,9 @@
 	public CBaseFilter,
 	public CAMThread,
+	public MediaContentSource,
 	public ISpecifyPropertyPages,
 	public IMusepackSplitter,
-	public IMediaSeeking
+	public IMediaSeeking,
+	public IAMStreamSelect
 {
 public:
@@ -42,4 +44,5 @@
 	double						rate;
 
+	IAMMediaContent				*content;
 
 public:
@@ -55,4 +58,7 @@
     // ISpecifyPropertyPages interface
     STDMETHODIMP GetPages(CAUUID *pPages);
+
+	// MediaContentSource
+	virtual int GetContentString(CString key, CString &value);
 
 	// CBaseFilter
@@ -78,4 +84,12 @@
 	STDMETHODIMP GetFileInfo(MPC_File_Info *info);
 	STDMETHODIMP SetPropertyPageWindow(HWND wnd);
+
+	// IAMStreamSelect (chapters hack)
+	STDMETHODIMP Count(DWORD *pcStreams);
+	STDMETHODIMP Info(long lIndex, AM_MEDIA_TYPE **ppmt, DWORD *pdwFlags,
+					  LCID *plcid, DWORD *pdwGroup, LPWSTR *ppszName, 
+					  IUnknown **ppObject, IUnknown **ppUnk);        
+	STDMETHODIMP Enable(long lIndex, DWORD dwFlags);
+
 
 	// activate / deactivate filter
Index: /dsfilters/demux_mpc/src/stdafx.h
===================================================================
--- /dsfilters/demux_mpc/src/stdafx.h	(revision 420)
+++ /dsfilters/demux_mpc/src/stdafx.h	(revision 421)
@@ -48,11 +48,19 @@
 #include <windows.h>
 
+#include <vector>
+
+using namespace std;
+
 #include <afxtempl.h>
 #include <streams.h>
 #include <initguid.h>
 #include <dvdmedia.h>
+#include <qnetwork.h>
+
 
 #include "bits.h"
 #include "mpc_types.h"
+#include "ape_tag.h"
+#include "media_content.h"
 #include "mpc_file.h"
 #include "mpc_pin.h"
