1 /*
2  * Copyright 2009 Vincent Povirk for CodeWeavers
3  * Copyright 2013 Ludger Sprenker
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "wincodecs_private.h"
21 
22 typedef struct PropertyBag {
23     IPropertyBag2 IPropertyBag2_iface;
24     LONG ref;
25     UINT prop_count;
26     PROPBAG2 *properties;
27     VARIANT *values;
28 } PropertyBag;
29 
30 static inline PropertyBag *impl_from_IPropertyBag2(IPropertyBag2 *iface)
31 {
32     return CONTAINING_RECORD(iface, PropertyBag, IPropertyBag2_iface);
33 }
34 
35 static HRESULT WINAPI PropertyBag_QueryInterface(IPropertyBag2 *iface, REFIID iid,
36     void **ppv)
37 {
38     PropertyBag *This = impl_from_IPropertyBag2(iface);
39     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
40 
41     if (!ppv) return E_INVALIDARG;
42 
43     if (IsEqualIID(&IID_IUnknown, iid) ||
44         IsEqualIID(&IID_IPropertyBag2, iid))
45     {
46         *ppv = &This->IPropertyBag2_iface;
47     }
48     else
49     {
50         *ppv = NULL;
51         return E_NOINTERFACE;
52     }
53 
54     IUnknown_AddRef((IUnknown*)*ppv);
55     return S_OK;
56 }
57 
58 static ULONG WINAPI PropertyBag_AddRef(IPropertyBag2 *iface)
59 {
60     PropertyBag *This = impl_from_IPropertyBag2(iface);
61     ULONG ref = InterlockedIncrement(&This->ref);
62 
63     TRACE("(%p) refcount=%u\n", iface, ref);
64 
65     return ref;
66 }
67 
68 static ULONG WINAPI PropertyBag_Release(IPropertyBag2 *iface)
69 {
70     PropertyBag *This = impl_from_IPropertyBag2(iface);
71     ULONG ref = InterlockedDecrement(&This->ref);
72 
73     TRACE("(%p) refcount=%u\n", iface, ref);
74 
75     if (ref == 0)
76     {
77         ULONG i;
78         if (This->properties && This->values)
79         {
80             for (i=0; i < This->prop_count; i++)
81             {
82                 CoTaskMemFree(This->properties[i].pstrName);
83                 VariantClear( This->values+i );
84             }
85         }
86 
87         HeapFree(GetProcessHeap(), 0, This->properties);
88         HeapFree(GetProcessHeap(), 0, This->values);
89         HeapFree(GetProcessHeap(), 0, This);
90     }
91 
92     return ref;
93 }
94 
95 static LONG find_item(PropertyBag *This, LPCOLESTR name)
96 {
97     LONG i;
98     if (!This->properties)
99         return -1;
100     if (!name)
101         return -1;
102 
103     for (i=0; i < This->prop_count; i++)
104     {
105         if (strcmpW(name, This->properties[i].pstrName) == 0)
106             return i;
107     }
108 
109     return -1;
110 }
111 
112 static HRESULT WINAPI PropertyBag_Read(IPropertyBag2 *iface, ULONG cProperties,
113     PROPBAG2 *pPropBag, IErrorLog *pErrLog, VARIANT *pvarValue, HRESULT *phrError)
114 {
115     HRESULT res = S_OK;
116     ULONG i;
117     PropertyBag *This = impl_from_IPropertyBag2(iface);
118 
119     TRACE("(%p,%u,%p,%p,%p,%p)\n", iface, cProperties, pPropBag, pErrLog, pvarValue, phrError);
120 
121     for (i=0; i < cProperties; i++)
122     {
123         LONG idx;
124         if (pPropBag[i].dwHint && pPropBag[i].dwHint <= This->prop_count)
125             idx = pPropBag[i].dwHint-1;
126         else
127             idx = find_item(This, pPropBag[i].pstrName);
128 
129         if (idx > -1)
130         {
131             VariantInit(pvarValue+i);
132             res = VariantCopy(pvarValue+i, This->values+idx);
133             if (FAILED(res))
134                 break;
135             phrError[i] = res;
136         }
137         else
138         {
139             res = E_FAIL;
140             break;
141         }
142     }
143 
144     return res;
145 }
146 
147 static HRESULT WINAPI PropertyBag_Write(IPropertyBag2 *iface, ULONG cProperties,
148     PROPBAG2 *pPropBag, VARIANT *pvarValue)
149 {
150     HRESULT res = S_OK;
151     ULONG i;
152     PropertyBag *This = impl_from_IPropertyBag2(iface);
153 
154     TRACE("(%p,%u,%p,%p)\n", iface, cProperties, pPropBag, pvarValue);
155 
156     for (i=0; i < cProperties; i++)
157     {
158         LONG idx;
159         if (pPropBag[i].dwHint && pPropBag[i].dwHint <= This->prop_count)
160             idx = pPropBag[i].dwHint-1;
161         else
162             idx = find_item(This, pPropBag[i].pstrName);
163 
164         if (idx > -1)
165         {
166             if (This->properties[idx].vt != V_VT(pvarValue+i))
167                 return WINCODEC_ERR_PROPERTYUNEXPECTEDTYPE;
168             res = VariantCopy(This->values+idx, pvarValue+i);
169             if (FAILED(res))
170                 return E_FAIL;
171         }
172         else
173         {
174             if (pPropBag[i].pstrName)
175                 FIXME("Application tried to set the unknown option %s.\n",
176                       debugstr_w(pPropBag[i].pstrName));
177 
178             /* FIXME: Function is not atomar on error, but MSDN does not say anything about it
179              *        (no reset of items between 0 and i-1) */
180             return E_FAIL;
181         }
182     }
183 
184     return res;
185 }
186 
187 static HRESULT WINAPI PropertyBag_CountProperties(IPropertyBag2 *iface, ULONG *pcProperties)
188 {
189     PropertyBag *This = impl_from_IPropertyBag2(iface);
190 
191     TRACE("(%p,%p)\n", iface, pcProperties);
192 
193     if (!pcProperties)
194         return E_INVALIDARG;
195 
196     *pcProperties = This->prop_count;
197 
198     return S_OK;
199 }
200 
201 static HRESULT copy_propbag2(PROPBAG2 *dest, PROPBAG2 *src)
202 {
203     dest->cfType = src->cfType;
204     dest->clsid = src->clsid;
205     dest->dwHint = src->dwHint;
206     dest->dwType = src->dwType;
207     dest->vt = src->vt;
208     dest->pstrName = CoTaskMemAlloc((strlenW(src->pstrName)+1) * sizeof(WCHAR));
209     if(!dest->pstrName)
210         return E_OUTOFMEMORY;
211 
212     strcpyW(dest->pstrName, src->pstrName);
213 
214     return S_OK;
215 }
216 
217 static HRESULT WINAPI PropertyBag_GetPropertyInfo(IPropertyBag2 *iface, ULONG iProperty,
218     ULONG cProperties, PROPBAG2 *pPropBag, ULONG *pcProperties)
219 {
220     HRESULT res = S_OK;
221     ULONG i;
222     PropertyBag *This = impl_from_IPropertyBag2(iface);
223 
224     TRACE("(%p,%u,%u,%p,%p)\n", iface, iProperty, cProperties, pPropBag, pcProperties);
225 
226     if (iProperty >= This->prop_count && iProperty > 0)
227         return WINCODEC_ERR_VALUEOUTOFRANGE;
228     if (iProperty+cProperties > This->prop_count )
229         return WINCODEC_ERR_VALUEOUTOFRANGE;
230 
231     *pcProperties = min(cProperties, This->prop_count-iProperty);
232 
233     for (i=0; i < *pcProperties; i++)
234     {
235         res = copy_propbag2(pPropBag+i, This->properties+iProperty+i);
236         if (FAILED(res))
237         {
238             do {
239                 CoTaskMemFree( pPropBag[--i].pstrName );
240             } while (i);
241             break;
242         }
243     }
244 
245     return res;
246 }
247 
248 static HRESULT WINAPI PropertyBag_LoadObject(IPropertyBag2 *iface, LPCOLESTR pstrName,
249     DWORD dwHint, IUnknown *pUnkObject, IErrorLog *pErrLog)
250 {
251     FIXME("(%p,%s,%u,%p,%p): stub\n", iface, debugstr_w(pstrName), dwHint, pUnkObject, pErrLog);
252     return E_NOTIMPL;
253 }
254 
255 static const IPropertyBag2Vtbl PropertyBag_Vtbl = {
256     PropertyBag_QueryInterface,
257     PropertyBag_AddRef,
258     PropertyBag_Release,
259     PropertyBag_Read,
260     PropertyBag_Write,
261     PropertyBag_CountProperties,
262     PropertyBag_GetPropertyInfo,
263     PropertyBag_LoadObject
264 };
265 
266 HRESULT CreatePropertyBag2(PROPBAG2 *options, UINT count,
267                            IPropertyBag2 **ppPropertyBag2)
268 {
269     UINT i;
270     HRESULT res = S_OK;
271     PropertyBag *This;
272 
273     This = HeapAlloc(GetProcessHeap(), 0, sizeof(PropertyBag));
274     if (!This) return E_OUTOFMEMORY;
275 
276     This->IPropertyBag2_iface.lpVtbl = &PropertyBag_Vtbl;
277     This->ref = 1;
278     This->prop_count = count;
279 
280     if (count == 0)
281     {
282         This->properties = NULL;
283         This->values = NULL;
284     }
285     else
286     {
287         This->properties = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PROPBAG2)*count);
288         This->values = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(VARIANT)*count);
289 
290         if (!This->properties || !This->values)
291             res = E_OUTOFMEMORY;
292         else
293             for (i=0; i < count; i++)
294             {
295                 res = copy_propbag2(This->properties+i, options+i);
296                 if (FAILED(res))
297                     break;
298                 This->properties[i].dwHint = i+1; /* 0 means unset, so we start with 1 */
299             }
300     }
301 
302     if (FAILED(res))
303     {
304         PropertyBag_Release(&This->IPropertyBag2_iface);
305         *ppPropertyBag2 = NULL;
306     }
307     else
308         *ppPropertyBag2 = &This->IPropertyBag2_iface;
309 
310     return res;
311 }
312