/*****************************************************************************
* persiststreaminit.cpp: ActiveX control for VLC
*****************************************************************************
- * Copyright (C) 2005 VideoLAN
+ * Copyright (C) 2005 the VideoLAN team
*
* Authors: Damien Fouilleul <Damien.Fouilleul@laposte.net>
*
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "plugin.h"
#include "persiststreaminit.h"
+#include "utils.h"
+#include <map>
+
+#include <malloc.h>
+#include <wchar.h>
+
using namespace std;
+class AxVLCVariant
+{
+
+public:
+
+ AxVLCVariant(void)
+ {
+ VariantInit(&_v);
+ };
+
+ ~AxVLCVariant(void)
+ {
+ VariantClear(&_v);
+ }
+
+ AxVLCVariant(VARIANTARG &v)
+ {
+ VariantInit(&_v);
+ VariantCopy(&_v, &v);
+ };
+
+ AxVLCVariant(VARIANTARG *v)
+ {
+ VariantInit(&_v);
+ VariantCopy(&_v, v);
+ };
+
+ AxVLCVariant(const AxVLCVariant &vv)
+ {
+ VariantInit(&_v);
+ VariantCopy(&_v, const_cast<VARIANTARG *>(&(vv._v)));
+ };
+
+ AxVLCVariant(int i)
+ {
+ V_VT(&_v) = VT_I4;
+ V_I4(&_v) = i;
+ };
+
+ AxVLCVariant(BSTR bstr)
+ {
+ VARIANT arg;
+ V_VT(&arg) = VT_BSTR;
+ V_BSTR(&arg) = bstr;
+ VariantInit(&_v);
+ VariantCopy(&_v, &arg);
+ };
+
+ inline const VARIANTARG *variantArg(void) const {
+ return &_v;
+ }
+
+ inline void swap(AxVLCVariant &v1, AxVLCVariant &v2)
+ {
+ VARIANTARG tmp = v1._v;
+ v1._v = v2._v;
+ v2._v = tmp;
+ };
+
+private:
+
+ VARIANTARG _v;
+};
+
+class AxVLCWSTR
+{
+
+public:
+
+ AxVLCWSTR(void) : _data(NULL) {};
+
+ virtual ~AxVLCWSTR()
+ {
+ if( NULL != _data )
+ {
+ ULONG refcount = InterlockedDecrement(&(_data->refcount));
+ if( 0 == refcount )
+ CoTaskMemFree(_data);
+ }
+ };
+
+ AxVLCWSTR(LPCWSTR s)
+ {
+ if( NULL != s )
+ {
+ size_t len = wcslen(s);
+ if( len > 0 )
+ {
+ size_t size = len*sizeof(WCHAR);
+ _data = (struct data *)CoTaskMemAlloc(sizeof(struct data)+size);
+ if( NULL != _data )
+ {
+ _data->len = len;
+ _data->refcount = 1;
+ memcpy(_data->wstr, s, size);
+ _data->wstr[len]=L'\0';
+ return;
+ }
+ }
+ }
+ _data = NULL;
+ };
+
+ AxVLCWSTR(const AxVLCWSTR &s)
+ {
+ _data = s._data;
+ if( NULL != _data )
+ InterlockedIncrement(&(_data->refcount));
+ };
+
+ inline bool operator<(const AxVLCWSTR &s) const
+ {
+ return compareNoCase(s.wstr()) < 0;
+ };
+
+ inline bool operator<(LPCWSTR s) const
+ {
+ return compareNoCase(s) < 0;
+ };
+
+ inline bool operator==(const AxVLCWSTR &s) const
+ {
+ return size() == s.size() ?
+ (compareNoCase(s.wstr()) == 0) : false;
+ };
+
+ inline bool operator==(LPCWSTR s) const
+ {
+ return compareNoCase(s) == 0;
+ };
+
+ LPCWSTR wstr(void) const
+ {
+ return (NULL != _data) ? _data->wstr : NULL;
+ };
+
+ size_t size(void) const
+ {
+ return (NULL != _data) ? _data->len : 0;
+ };
+
+private:
+
+ inline int compareNoCase(LPCWSTR s) const
+ {
+ if( NULL == _data )
+ {
+ return (NULL == s) ? 0 : -1;
+ }
+ if( NULL == s )
+ return 1;
+
+ return _wcsicmp(_data->wstr, s);
+ };
+
+ struct data {
+ size_t len;
+ LONG refcount;
+ wchar_t wstr[1];
+ } *_data;
+};
+
+typedef pair<class AxVLCWSTR, class AxVLCVariant> AxVLCPropertyPair;
+typedef map<class AxVLCWSTR, class AxVLCVariant> AxVLCPropertyMap;
+
+///////////////////////////
+
+class VLCPropertyBag : public IPropertyBag
+{
+
+public:
+
+ VLCPropertyBag(void) : _i_ref(1) {};
+ virtual ~VLCPropertyBag() {};
+
+ // IUnknown methods
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
+ {
+ if( NULL == ppv )
+ return E_POINTER;
+ if( (IID_IUnknown == riid)
+ || (IID_IPropertyBag == riid) )
+ {
+ AddRef();
+ *ppv = reinterpret_cast<LPVOID>(this);
+ return NOERROR;
+ }
+ // standalone object
+ return E_NOINTERFACE;
+ };
+
+ STDMETHODIMP_(ULONG) AddRef(void)
+ { return InterlockedIncrement(&_i_ref); };
+
+ STDMETHODIMP_(ULONG) Release(void)
+ {
+ ULONG refcount = InterlockedDecrement(&_i_ref);
+ if( 0 == refcount )
+ {
+ delete this;
+ return 0;
+ }
+ return refcount;
+ };
+
+ // IPropertyBag methods
+
+ STDMETHODIMP Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog)
+ {
+ if( (NULL == pszPropName) || (NULL == pVar) )
+ return E_POINTER;
+
+ AxVLCPropertyMap::const_iterator notfound = _pm.end();
+ AxVLCPropertyMap::const_iterator iter = _pm.find(pszPropName);
+ if( notfound != iter )
+ {
+ VARTYPE vtype = V_VT(pVar);
+ VARIANTARG v;
+ VariantInit(&v);
+ VariantCopy(&v, const_cast<VARIANTARG*>((*iter).second.variantArg()));
+ if( (V_VT(&v) != vtype) && FAILED(VariantChangeType(&v, &v, 0, vtype)) )
+ {
+ VariantClear(&v);
+ return E_FAIL;
+ }
+ *pVar = v;
+ return S_OK;
+ }
+ else
+ return E_INVALIDARG;
+ };
+
+ STDMETHODIMP Write(LPCOLESTR pszPropName, VARIANT *pVar)
+ {
+ if( (NULL == pszPropName) || (NULL == pVar) )
+ return E_POINTER;
+
+ AxVLCPropertyPair val(pszPropName, pVar);
+ pair<AxVLCPropertyMap::iterator, bool> p = _pm.insert(val);
+ if( false == p.second )
+ // replace existing key value
+ (*p.first).second = val.second;
+ return S_OK;
+ };
+
+ // custom methods
+
+ HRESULT Load(LPSTREAM pStm)
+ {
+ if( NULL == pStm )
+ return E_INVALIDARG;
+
+ HRESULT result;
+
+ AxVLCPropertyPair *val;
+ result = ReadProperty(pStm, &val);
+ if( SUCCEEDED(result) )
+ {
+ if( (val->first == L"(Count)") && (VT_I4 == V_VT(val->second.variantArg())) )
+ {
+ size_t count = V_I4(val->second.variantArg());
+ delete val;
+ while( count-- )
+ {
+ result = ReadProperty(pStm, &val);
+ if( FAILED(result) )
+ return result;
+
+ pair<AxVLCPropertyMap::iterator, bool> p = _pm.insert(*val);
+ if( false == p.second )
+ // replace existing key value
+ (*p.first).second = val->second;
+ delete val;
+ }
+ }
+ }
+ return result;
+ };
+
+ HRESULT Save(LPSTREAM pStm)
+ {
+ if( NULL == pStm )
+ return E_INVALIDARG;
+
+ HRESULT result;
+
+ AxVLCPropertyPair header(L"(Count)", _pm.size());
+ result = WriteProperty(pStm, header);
+ if( SUCCEEDED(result) )
+ {
+ AxVLCPropertyMap::const_iterator iter = _pm.begin();
+ AxVLCPropertyMap::const_iterator end = _pm.end();
+
+ while( iter != end )
+ {
+ result = WriteProperty(pStm, *(iter++));
+ if( FAILED(result) )
+ return result;
+ }
+ }
+ return result;
+ };
+
+ BOOL IsEmpty()
+ {
+ return _pm.size() == 0;
+ }
+
+private:
+
+ HRESULT WriteProperty(LPSTREAM pStm, const AxVLCPropertyPair &prop)
+ {
+ HRESULT result;
+
+ const AxVLCWSTR propName = prop.first;
+
+ ULONG len = propName.size();
+
+ if( 0 == len )
+ return E_INVALIDARG;
+
+ result = pStm->Write(&len, sizeof(len), NULL);
+ if( FAILED(result) )
+ return result;
+
+ result = pStm->Write(propName.wstr(), len*sizeof(WCHAR), NULL);
+ if( FAILED(result) )
+ return result;
+
+ const VARIANTARG *propValue = prop.second.variantArg();
+ VARTYPE vtype = V_VT(propValue);
+ switch( vtype )
+ {
+ case VT_BOOL:
+ result = pStm->Write(&vtype, sizeof(vtype), NULL);
+ if( FAILED(result) )
+ return result;
+ result = pStm->Write(&V_BOOL(propValue), sizeof(V_BOOL(propValue)), NULL);
+ if( FAILED(result) )
+ return result;
+ break;
+ case VT_I4:
+ result = pStm->Write(&vtype, sizeof(vtype), NULL);
+ if( FAILED(result) )
+ return result;
+ result = pStm->Write(&V_I4(propValue), sizeof(V_I4(propValue)), NULL);
+ if( FAILED(result) )
+ return result;
+ break;
+ case VT_BSTR:
+ result = pStm->Write(&vtype, sizeof(vtype), NULL);
+ if( FAILED(result) )
+ return result;
+ len = SysStringLen(V_BSTR(propValue));
+ result = pStm->Write(&len, sizeof(len), NULL);
+ if( FAILED(result) )
+ return result;
+ if( len > 0 )
+ {
+ result = pStm->Write(V_BSTR(propValue), len*sizeof(OLECHAR), NULL);
+ if( FAILED(result) )
+ return result;
+ }
+ break;
+ default:
+ vtype = VT_EMPTY;
+ result = pStm->Write(&vtype, sizeof(vtype), NULL);
+ if( FAILED(result) )
+ return result;
+ }
+ return result;
+ };
+
+ HRESULT ReadProperty(LPSTREAM pStm, AxVLCPropertyPair **prop)
+ {
+ HRESULT result;
+
+ ULONG len;
+
+ result = pStm->Read(&len, sizeof(len), NULL);
+ if( FAILED(result) )
+ return result;
+
+ if( 0 == len )
+ return E_INVALIDARG;
+
+ LPWSTR propName = (LPOLESTR)::alloca((len+1)*sizeof(WCHAR));
+ if( NULL == propName )
+ return E_OUTOFMEMORY;
+
+ result = pStm->Read(propName, len*sizeof(WCHAR), NULL);
+ if( FAILED(result) )
+ return result;
+
+ propName[len] = L'\0';
+
+ VARIANTARG propValue;
+
+ VARTYPE vtype;
+ result = pStm->Read(&vtype, sizeof(vtype), NULL);
+ if( FAILED(result) )
+ return result;
+
+ switch( vtype )
+ {
+ case VT_BOOL:
+ V_VT(&propValue) = vtype;
+ result = pStm->Read(&V_BOOL(&propValue), sizeof(V_BOOL(&propValue)), NULL);
+ if( FAILED(result) )
+ return result;
+ break;
+ case VT_I4:
+ V_VT(&propValue) = vtype;
+ result = pStm->Read(&V_I4(&propValue), sizeof(V_I4(&propValue)), NULL);
+ if( FAILED(result) )
+ return result;
+ break;
+ case VT_BSTR:
+ V_VT(&propValue) = vtype;
+ result = pStm->Read(&len, sizeof(len), NULL);
+ if( FAILED(result) )
+ return result;
+
+ V_BSTR(&propValue) = NULL;
+ if( len > 0 )
+ {
+ V_BSTR(&propValue) = SysAllocStringLen(NULL, len);
+ if( NULL == V_BSTR(&propValue) )
+ return E_OUTOFMEMORY;
+
+ result = pStm->Read(V_BSTR(&propValue), len*sizeof(OLECHAR), NULL);
+ if( FAILED(result) )
+ {
+ SysFreeString(V_BSTR(&propValue));
+ return result;
+ }
+ }
+ break;
+ default:
+ VariantInit(&propValue);
+ }
+
+ *prop = new AxVLCPropertyPair(propName, propValue);
+
+ return S_OK;
+ };
+
+ AxVLCPropertyMap _pm;
+ LONG _i_ref;
+};
+
+///////////////////////////
+
+VLCPersistStreamInit::VLCPersistStreamInit(VLCPlugin *p_instance) : _p_instance(p_instance)
+{
+ _p_props = new VLCPropertyBag();
+};
+
+VLCPersistStreamInit::~VLCPersistStreamInit()
+{
+ _p_props->Release();
+};
+
STDMETHODIMP VLCPersistStreamInit::GetClassID(LPCLSID pClsID)
{
if( NULL == pClsID )
STDMETHODIMP VLCPersistStreamInit::InitNew(void)
{
- return _p_instance->onInit(TRUE);
+ return _p_instance->onInit();
};
STDMETHODIMP VLCPersistStreamInit::Load(LPSTREAM pStm)
{
- if( NULL == pStm )
- return E_POINTER;
+ HRESULT result = _p_props->Load(pStm);
+ if( FAILED(result) )
+ return result;
- return _p_instance->onInit(TRUE);
+ LPPERSISTPROPERTYBAG pPersistPropBag;
+ if( FAILED(QueryInterface(IID_IPersistPropertyBag, (void**)&pPersistPropBag)) )
+ return E_FAIL;
+
+ result = pPersistPropBag->Load(_p_props, NULL);
+ pPersistPropBag->Release();
+
+ return result;
};
STDMETHODIMP VLCPersistStreamInit::Save(LPSTREAM pStm, BOOL fClearDirty)
{
if( NULL == pStm )
- return E_POINTER;
+ return E_INVALIDARG;
- return S_OK;
+ LPPERSISTPROPERTYBAG pPersistPropBag;
+ if( FAILED(QueryInterface(IID_IPersistPropertyBag, (void**)&pPersistPropBag)) )
+ return E_FAIL;
+
+ HRESULT result = pPersistPropBag->Save(_p_props, fClearDirty, _p_props->IsEmpty());
+ pPersistPropBag->Release();
+ if( FAILED(result) )
+ return result;
+
+ return _p_props->Save(pStm);
};
STDMETHODIMP VLCPersistStreamInit::IsDirty(void)
{
- return S_FALSE;
+ return _p_instance->isDirty() ? S_OK : S_FALSE;
};
STDMETHODIMP VLCPersistStreamInit::GetSizeMax(ULARGE_INTEGER *pcbSize)
{
- pcbSize->QuadPart = 0ULL;
+ pcbSize->HighPart = 0UL;
+ pcbSize->LowPart = 16384UL; // just a guess
return S_OK;
};