]> git.sesse.net Git - vlc/blobdiff - activex/persiststreaminit.cpp
Untested support for RFC4771:
[vlc] / activex / persiststreaminit.cpp
index 0940b29d4880ad1cb9486d1cc1582e37870cb2c4..8d41fb2bff5b8a74869281245a8bfec177580392 100644 (file)
@@ -1,7 +1,7 @@
 /*****************************************************************************
  * 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 )
@@ -37,33 +506,51 @@ STDMETHODIMP VLCPersistStreamInit::GetClassID(LPCLSID 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;
 };