wbemprox: Add stub implementations of IEnumWbemClassObject and IWbemClassObject.
[wine] / dlls / quartz / filtermapper.c
1 /*
2  * IFilterMapper & IFilterMapper3 Implementations
3  *
4  * Copyright 2003 Robert Shearman
5  * Copyright 2004 Christian Costa
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winreg.h"
30 #include "winerror.h"
31
32 #include "quartz_private.h"
33
34 #include "ole2.h"
35 #include "olectl.h"
36 #include "strmif.h"
37 #include "wine/unicode.h"
38 #include "uuids.h"
39 #include "initguid.h"
40 #include "fil_data.h"
41
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
45
46 #define ARRAYSIZE(array) (sizeof(array)/sizeof((array)[0]))
47
48 typedef struct FilterMapper3Impl
49 {
50     IFilterMapper3 IFilterMapper3_iface;
51     IFilterMapper IFilterMapper_iface;
52     IAMFilterData IAMFilterData_iface;
53     const IUnknownVtbl *IInner_vtbl;
54     LONG refCount;
55     IUnknown * pUnkOuter;
56     BOOL bUnkOuterValid;
57     BOOL bAggregatable;
58 } FilterMapper3Impl;
59
60 static const IUnknownVtbl IInner_VTable;
61 static const IFilterMapper3Vtbl fm3vtbl;
62 static const IFilterMapperVtbl fmvtbl;
63 static const IAMFilterDataVtbl AMFilterDataVtbl;
64
65 static inline FilterMapper3Impl *impl_from_IFilterMapper3( IFilterMapper3 *iface )
66 {
67     return CONTAINING_RECORD(iface, FilterMapper3Impl, IFilterMapper3_iface);
68 }
69
70 static inline FilterMapper3Impl *impl_from_IFilterMapper( IFilterMapper *iface )
71 {
72     return CONTAINING_RECORD(iface, FilterMapper3Impl, IFilterMapper_iface);
73 }
74
75 static inline FilterMapper3Impl *impl_from_IAMFilterData( IAMFilterData *iface )
76 {
77     return CONTAINING_RECORD(iface, FilterMapper3Impl, IAMFilterData_iface);
78 }
79
80 static inline FilterMapper3Impl *impl_from_inner_IUnknown( IUnknown *iface )
81 {
82     return (FilterMapper3Impl *)((char*)iface - FIELD_OFFSET(FilterMapper3Impl, IInner_vtbl));
83 }
84
85 static const WCHAR wszClsidSlash[] = {'C','L','S','I','D','\\',0};
86 static const WCHAR wszSlashInstance[] = {'\\','I','n','s','t','a','n','c','e','\\',0};
87 static const WCHAR wszSlash[] = {'\\',0};
88
89 /* CLSID property in media category Moniker */
90 static const WCHAR wszClsidName[] = {'C','L','S','I','D',0};
91 /* FriendlyName property in media category Moniker */
92 static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
93 /* Merit property in media category Moniker (CLSID_ActiveMovieCategories only) */
94 static const WCHAR wszMeritName[] = {'M','e','r','i','t',0};
95 /* FilterData property in media category Moniker (not CLSID_ActiveMovieCategories) */
96 static const WCHAR wszFilterDataName[] = {'F','i','l','t','e','r','D','a','t','a',0};
97 /* For filters registered with IFilterMapper */
98 static const WCHAR wszFilterSlash[] = {'F','i','l','t','e','r','\\',0};
99 static const WCHAR wszFilter[] = {'F','i','l','t','e','r',0};
100 /* For pins registered with IFilterMapper */
101 static const WCHAR wszPins[] = {'P','i','n','s',0};
102 static const WCHAR wszAllowedMany[] = {'A','l','l','o','w','e','d','M','a','n','y',0};
103 static const WCHAR wszAllowedZero[] = {'A','l','l','o','w','e','d','Z','e','r','o',0};
104 static const WCHAR wszDirection[] = {'D','i','r','e','c','t','i','o','n',0};
105 static const WCHAR wszIsRendered[] = {'I','s','R','e','n','d','e','r','e','d',0};
106 /* For types registered with IFilterMapper */
107 static const WCHAR wszTypes[] = {'T','y','p','e','s',0};
108
109
110 /* registry format for REGFILTER2 */
111 struct REG_RF
112 {
113     DWORD dwVersion;
114     DWORD dwMerit;
115     DWORD dwPins;
116     DWORD dwUnused;
117 };
118
119 struct REG_RFP
120 {
121     BYTE signature[4]; /* e.g. "0pi3" */
122     DWORD dwFlags;
123     DWORD dwInstances;
124     DWORD dwMediaTypes;
125     DWORD dwMediums;
126     DWORD bCategory; /* is there a category clsid? */
127     /* optional: dwOffsetCategoryClsid */
128 };
129
130 struct REG_TYPE
131 {
132     BYTE signature[4]; /* e.g. "0ty3" */
133     DWORD dwUnused;
134     DWORD dwOffsetMajor;
135     DWORD dwOffsetMinor;
136 };
137
138 struct MONIKER_MERIT
139 {
140     IMoniker * pMoniker;
141     DWORD dwMerit;
142 };
143
144 struct Vector
145 {
146     LPBYTE pData;
147     int capacity; /* in bytes */
148     int current; /* pointer to next free byte */
149 };
150
151 /* returns the position it was added at */
152 static int add_data(struct Vector * v, const BYTE * pData, int size)
153 {
154     int index = v->current;
155     if (v->current + size > v->capacity)
156     {
157         LPBYTE pOldData = v->pData;
158         v->capacity = (v->capacity + size) * 2;
159         v->pData = CoTaskMemAlloc(v->capacity);
160         memcpy(v->pData, pOldData, v->current);
161         CoTaskMemFree(pOldData);
162     }
163     memcpy(v->pData + v->current, pData, size);
164     v->current += size;
165     return index;
166 }
167
168 static int find_data(const struct Vector * v, const BYTE * pData, int size)
169 {
170     int index;
171     for (index = 0; index < v->current; index++)
172         if (!memcmp(v->pData + index, pData, size))
173             return index;
174     /* not found */
175     return -1;
176 }
177
178 static void delete_vector(struct Vector * v)
179 {
180     CoTaskMemFree(v->pData);
181     v->current = 0;
182     v->capacity = 0;
183 }
184
185 HRESULT FilterMapper2_create(IUnknown *pUnkOuter, LPVOID *ppObj)
186 {
187     FilterMapper3Impl * pFM2impl;
188
189     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
190
191     pFM2impl = CoTaskMemAlloc(sizeof(*pFM2impl));
192     if (!pFM2impl)
193         return E_OUTOFMEMORY;
194
195     pFM2impl->pUnkOuter = pUnkOuter;
196     pFM2impl->bUnkOuterValid = FALSE;
197     pFM2impl->bAggregatable = FALSE;
198     pFM2impl->IInner_vtbl = &IInner_VTable;
199     pFM2impl->IFilterMapper3_iface.lpVtbl = &fm3vtbl;
200     pFM2impl->IFilterMapper_iface.lpVtbl = &fmvtbl;
201     pFM2impl->IAMFilterData_iface.lpVtbl = &AMFilterDataVtbl;
202     pFM2impl->refCount = 1;
203
204     *ppObj = pFM2impl;
205
206     TRACE("-- created at %p\n", pFM2impl);
207
208     return S_OK;
209 }
210
211 HRESULT FilterMapper_create(IUnknown *pUnkOuter, LPVOID *ppObj)
212 {
213     FilterMapper3Impl *pFM2impl;
214     HRESULT hr;
215
216     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
217
218     hr = FilterMapper2_create(pUnkOuter, (LPVOID*)&pFM2impl);
219     if (FAILED(hr))
220         return hr;
221
222     *ppObj = &pFM2impl->IFilterMapper_iface;
223
224     return hr;
225 }
226
227 /*** IUnknown (inner) methods ***/
228
229 static HRESULT WINAPI Inner_QueryInterface(IUnknown * iface, REFIID riid, LPVOID * ppv)
230 {
231     FilterMapper3Impl *This = impl_from_inner_IUnknown(iface);
232     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
233
234     if (This->bAggregatable)
235         This->bUnkOuterValid = TRUE;
236
237     *ppv = NULL;
238
239     if (IsEqualIID(riid, &IID_IUnknown))
240         *ppv = &This->IInner_vtbl;
241     else if (IsEqualIID(riid, &IID_IFilterMapper2) ||
242         IsEqualIID(riid, &IID_IFilterMapper3))
243         *ppv = This;
244     else if (IsEqualIID(riid, &IID_IFilterMapper))
245         *ppv = &This->IFilterMapper_iface;
246     else if (IsEqualIID(riid, &IID_IAMFilterData))
247         *ppv = &This->IAMFilterData_iface;
248
249     if (*ppv != NULL)
250     {
251         IUnknown_AddRef((IUnknown *)*ppv);
252         return S_OK;
253     }
254
255     FIXME("No interface for %s\n", debugstr_guid(riid));
256     return E_NOINTERFACE;
257 }
258
259 static ULONG WINAPI Inner_AddRef(IUnknown * iface)
260 {
261     FilterMapper3Impl *This = impl_from_inner_IUnknown(iface);
262     ULONG refCount = InterlockedIncrement(&This->refCount);
263
264     TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
265
266     return refCount;
267 }
268
269 static ULONG WINAPI Inner_Release(IUnknown * iface)
270 {
271     FilterMapper3Impl *This = impl_from_inner_IUnknown(iface);
272     ULONG refCount = InterlockedDecrement(&This->refCount);
273
274     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
275
276     if (refCount == 0)
277     {
278         CoTaskMemFree(This);
279         return 0;
280     }
281     return refCount;
282 }
283
284 static const IUnknownVtbl IInner_VTable =
285 {
286     Inner_QueryInterface,
287     Inner_AddRef,
288     Inner_Release
289 };
290
291 static HRESULT WINAPI FilterMapper3_QueryInterface(IFilterMapper3 * iface, REFIID riid, LPVOID *ppv)
292 {
293     FilterMapper3Impl *This = impl_from_IFilterMapper3(iface);
294
295     if (This->bAggregatable)
296         This->bUnkOuterValid = TRUE;
297
298     if (This->pUnkOuter)
299     {
300         if (This->bAggregatable)
301             return IUnknown_QueryInterface(This->pUnkOuter, riid, ppv);
302
303         if (IsEqualIID(riid, &IID_IUnknown))
304         {
305             HRESULT hr;
306
307             IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
308             hr = IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
309             IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
310             This->bAggregatable = TRUE;
311             return hr;
312         }
313
314         *ppv = NULL;
315         return E_NOINTERFACE;
316     }
317
318     return IUnknown_QueryInterface((IUnknown *)&(This->IInner_vtbl), riid, ppv);
319 }
320
321 static ULONG WINAPI FilterMapper3_AddRef(IFilterMapper3 * iface)
322 {
323     FilterMapper3Impl *This = impl_from_IFilterMapper3(iface);
324
325     if (This->pUnkOuter && This->bUnkOuterValid)
326         return IUnknown_AddRef(This->pUnkOuter);
327     return IUnknown_AddRef((IUnknown *)&(This->IInner_vtbl));
328 }
329
330 static ULONG WINAPI FilterMapper3_Release(IFilterMapper3 * iface)
331 {
332     FilterMapper3Impl *This = impl_from_IFilterMapper3(iface);
333
334     if (This->pUnkOuter && This->bUnkOuterValid)
335         return IUnknown_Release(This->pUnkOuter);
336     return IUnknown_Release((IUnknown *)&(This->IInner_vtbl));
337 }
338
339 /*** IFilterMapper3 methods ***/
340
341 static HRESULT WINAPI FilterMapper3_CreateCategory(
342     IFilterMapper3 * iface,
343     REFCLSID clsidCategory,
344     DWORD dwCategoryMerit,
345     LPCWSTR szDescription)
346 {
347     LPWSTR wClsidAMCat = NULL;
348     LPWSTR wClsidCategory = NULL;
349     WCHAR wszKeyName[ARRAYSIZE(wszClsidSlash)-1 + ARRAYSIZE(wszSlashInstance)-1 + (CHARS_IN_GUID-1) * 2 + 1];
350     HKEY hKey = NULL;
351     LONG lRet;
352     HRESULT hr;
353
354     TRACE("(%s, %x, %s)\n", debugstr_guid(clsidCategory), dwCategoryMerit, debugstr_w(szDescription));
355
356     hr = StringFromCLSID(&CLSID_ActiveMovieCategories, &wClsidAMCat);
357
358     if (SUCCEEDED(hr))
359     {
360         hr = StringFromCLSID(clsidCategory, &wClsidCategory);
361     }
362
363     if (SUCCEEDED(hr))
364     {
365         strcpyW(wszKeyName, wszClsidSlash);
366         strcatW(wszKeyName, wClsidAMCat);
367         strcatW(wszKeyName, wszSlashInstance);
368         strcatW(wszKeyName, wClsidCategory);
369
370         lRet = RegCreateKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
371         hr = HRESULT_FROM_WIN32(lRet);
372     }
373
374     if (SUCCEEDED(hr))
375     {
376         lRet = RegSetValueExW(hKey, wszFriendlyName, 0, REG_SZ, (const BYTE*)szDescription, (strlenW(szDescription) + 1) * sizeof(WCHAR));
377         hr = HRESULT_FROM_WIN32(lRet);
378     }
379
380     if (SUCCEEDED(hr))
381     {
382         lRet = RegSetValueExW(hKey, wszClsidName, 0, REG_SZ, (LPBYTE)wClsidCategory, (strlenW(wClsidCategory) + 1) * sizeof(WCHAR));
383         hr = HRESULT_FROM_WIN32(lRet);
384     }
385
386     if (SUCCEEDED(hr))
387     {
388         lRet = RegSetValueExW(hKey, wszMeritName, 0, REG_DWORD, (LPBYTE)&dwCategoryMerit, sizeof(dwCategoryMerit));
389         hr = HRESULT_FROM_WIN32(lRet);
390     }
391
392     CloseHandle(hKey);
393     CoTaskMemFree(wClsidCategory);
394     CoTaskMemFree(wClsidAMCat);
395
396     return hr;
397 }
398
399 static HRESULT WINAPI FilterMapper3_UnregisterFilter(
400     IFilterMapper3 * iface,
401     const CLSID *pclsidCategory,
402     const OLECHAR *szInstance,
403     REFCLSID Filter)
404 {
405     WCHAR wszKeyName[MAX_PATH];
406     LPWSTR wClsidCategory = NULL;
407     LPWSTR wFilter = NULL;
408     HRESULT hr;
409
410     TRACE("(%p, %s, %s)\n", pclsidCategory, debugstr_w(szInstance), debugstr_guid(Filter));
411
412     if (!pclsidCategory)
413         pclsidCategory = &CLSID_LegacyAmFilterCategory;
414
415     hr = StringFromCLSID(pclsidCategory, &wClsidCategory);
416
417     if (SUCCEEDED(hr))
418     {
419         strcpyW(wszKeyName, wszClsidSlash);
420         strcatW(wszKeyName, wClsidCategory);
421         strcatW(wszKeyName, wszSlashInstance);
422         if (szInstance)
423             strcatW(wszKeyName, szInstance);
424         else
425         {
426             hr = StringFromCLSID(Filter, &wFilter);
427             if (SUCCEEDED(hr))
428                 strcatW(wszKeyName, wFilter);
429         }
430     }
431
432     if (SUCCEEDED(hr))
433     {
434         LONG lRet = RegDeleteKeyW(HKEY_CLASSES_ROOT, wszKeyName);
435         hr = HRESULT_FROM_WIN32(lRet);
436     }
437
438     CoTaskMemFree(wClsidCategory);
439     CoTaskMemFree(wFilter);
440
441     return hr;
442 }
443
444 static HRESULT FM2_WriteFriendlyName(IPropertyBag * pPropBag, LPCWSTR szName)
445 {
446     VARIANT var;
447     HRESULT ret;
448     BSTR value;
449
450     V_VT(&var) = VT_BSTR;
451     V_UNION(&var, bstrVal) = value = SysAllocString(szName);
452
453     ret = IPropertyBag_Write(pPropBag, wszFriendlyName, &var);
454     SysFreeString(value);
455
456     return ret;
457 }
458
459 static HRESULT FM2_WriteClsid(IPropertyBag * pPropBag, REFCLSID clsid)
460 {
461     LPWSTR wszClsid = NULL;
462     VARIANT var;
463     HRESULT hr;
464
465     hr = StringFromCLSID(clsid, &wszClsid);
466
467     if (SUCCEEDED(hr))
468     {
469         V_VT(&var) = VT_BSTR;
470         V_UNION(&var, bstrVal) = wszClsid;
471         hr = IPropertyBag_Write(pPropBag, wszClsidName, &var);
472     }
473     CoTaskMemFree(wszClsid);
474     return hr;
475 }
476
477 static HRESULT FM2_WriteFilterData(const REGFILTER2 * prf2, BYTE **ppData, ULONG *pcbData)
478 {
479     int size = sizeof(struct REG_RF);
480     unsigned int i;
481     struct Vector mainStore = {NULL, 0, 0};
482     struct Vector clsidStore = {NULL, 0, 0};
483     struct REG_RF rrf;
484     HRESULT hr = S_OK;
485
486     rrf.dwVersion = prf2->dwVersion;
487     rrf.dwMerit = prf2->dwMerit;
488     rrf.dwPins = prf2->u.s2.cPins2;
489     rrf.dwUnused = 0;
490
491     add_data(&mainStore, (LPBYTE)&rrf, sizeof(rrf));
492
493     for (i = 0; i < prf2->u.s2.cPins2; i++)
494     {
495         size += sizeof(struct REG_RFP);
496         if (prf2->u.s2.rgPins2[i].clsPinCategory)
497             size += sizeof(DWORD);
498         size += prf2->u.s2.rgPins2[i].nMediaTypes * sizeof(struct REG_TYPE);
499         size += prf2->u.s2.rgPins2[i].nMediums * sizeof(DWORD);
500     }
501
502     for (i = 0; i < prf2->u.s2.cPins2; i++)
503     {
504         struct REG_RFP rrfp;
505         REGFILTERPINS2 rgPin2 = prf2->u.s2.rgPins2[i];
506         unsigned int j;
507
508         rrfp.signature[0] = '0';
509         rrfp.signature[1] = 'p';
510         rrfp.signature[2] = 'i';
511         rrfp.signature[3] = '3';
512         rrfp.signature[0] += i;
513         rrfp.dwFlags = rgPin2.dwFlags;
514         rrfp.dwInstances = rgPin2.cInstances;
515         rrfp.dwMediaTypes = rgPin2.nMediaTypes;
516         rrfp.dwMediums = rgPin2.nMediums;
517         rrfp.bCategory = rgPin2.clsPinCategory ? 1 : 0;
518
519         add_data(&mainStore, (LPBYTE)&rrfp, sizeof(rrfp));
520         if (rrfp.bCategory)
521         {
522             DWORD index = find_data(&clsidStore, (const BYTE*)rgPin2.clsPinCategory, sizeof(CLSID));
523             if (index == -1)
524                 index = add_data(&clsidStore, (const BYTE*)rgPin2.clsPinCategory, sizeof(CLSID));
525             index += size;
526
527             add_data(&mainStore, (LPBYTE)&index, sizeof(index));
528         }
529
530         for (j = 0; j < rgPin2.nMediaTypes; j++)
531         {
532             struct REG_TYPE rt;
533             const CLSID * clsMinorType = rgPin2.lpMediaType[j].clsMinorType ? rgPin2.lpMediaType[j].clsMinorType : &MEDIASUBTYPE_NULL;
534             rt.signature[0] = '0';
535             rt.signature[1] = 't';
536             rt.signature[2] = 'y';
537             rt.signature[3] = '3';
538             rt.signature[0] += j;
539             rt.dwUnused = 0;
540             rt.dwOffsetMajor = find_data(&clsidStore, (const BYTE*)rgPin2.lpMediaType[j].clsMajorType, sizeof(CLSID));
541             if (rt.dwOffsetMajor == -1)
542                 rt.dwOffsetMajor = add_data(&clsidStore, (const BYTE*)rgPin2.lpMediaType[j].clsMajorType, sizeof(CLSID));
543             rt.dwOffsetMajor += size;
544             rt.dwOffsetMinor = find_data(&clsidStore, (const BYTE*)clsMinorType, sizeof(CLSID));
545             if (rt.dwOffsetMinor == -1)
546                 rt.dwOffsetMinor = add_data(&clsidStore, (const BYTE*)clsMinorType, sizeof(CLSID));
547             rt.dwOffsetMinor += size;
548
549             add_data(&mainStore, (LPBYTE)&rt, sizeof(rt));
550         }
551
552         for (j = 0; j < rgPin2.nMediums; j++)
553         {
554             DWORD index = find_data(&clsidStore, (const BYTE*)(rgPin2.lpMedium + j), sizeof(REGPINMEDIUM));
555             if (index == -1)
556                 index = add_data(&clsidStore, (const BYTE*)(rgPin2.lpMedium + j), sizeof(REGPINMEDIUM));
557             index += size;
558
559             add_data(&mainStore, (LPBYTE)&index, sizeof(index));
560         }
561     }
562
563     if (SUCCEEDED(hr))
564     {
565         *pcbData = mainStore.current + clsidStore.current;
566         *ppData = CoTaskMemAlloc(*pcbData);
567         if (!*ppData)
568             hr = E_OUTOFMEMORY;
569     }
570
571     if (SUCCEEDED(hr))
572     {
573         memcpy(*ppData, mainStore.pData, mainStore.current);
574         memcpy((*ppData) + mainStore.current, clsidStore.pData, clsidStore.current);
575     }
576
577     delete_vector(&mainStore);
578     delete_vector(&clsidStore);
579     return hr;
580 }
581
582 static HRESULT FM2_ReadFilterData(BYTE *pData, REGFILTER2 * prf2)
583 {
584     HRESULT hr = S_OK;
585     struct REG_RF * prrf;
586     LPBYTE pCurrent;
587     DWORD i;
588     REGFILTERPINS2 * rgPins2;
589
590     prrf = (struct REG_RF *)pData;
591     pCurrent = pData;
592
593     if (prrf->dwVersion != 2)
594     {
595         FIXME("Filter registry version %d not supported\n", prrf->dwVersion);
596         ZeroMemory(prf2, sizeof(*prf2));
597         hr = E_FAIL;
598     }
599
600     if (SUCCEEDED(hr))
601     {
602         TRACE("version = %d, merit = %x, #pins = %d, unused = %x\n",
603             prrf->dwVersion, prrf->dwMerit, prrf->dwPins, prrf->dwUnused);
604
605         prf2->dwVersion = prrf->dwVersion;
606         prf2->dwMerit = prrf->dwMerit;
607         prf2->u.s2.cPins2 = prrf->dwPins;
608         rgPins2 = CoTaskMemAlloc(prrf->dwPins * sizeof(*rgPins2));
609         prf2->u.s2.rgPins2 = rgPins2;
610         pCurrent += sizeof(struct REG_RF);
611
612         for (i = 0; i < prrf->dwPins; i++)
613         {
614             struct REG_RFP * prrfp = (struct REG_RFP *)pCurrent;
615             REGPINTYPES * lpMediaType;
616             REGPINMEDIUM * lpMedium;
617             UINT j;
618
619             /* FIXME: check signature */
620
621             TRACE("\tsignature = %s\n", debugstr_an((const char*)prrfp->signature, 4));
622
623             TRACE("\tpin[%d]: flags = %x, instances = %d, media types = %d, mediums = %d\n",
624                 i, prrfp->dwFlags, prrfp->dwInstances, prrfp->dwMediaTypes, prrfp->dwMediums);
625
626             rgPins2[i].dwFlags = prrfp->dwFlags;
627             rgPins2[i].cInstances = prrfp->dwInstances;
628             rgPins2[i].nMediaTypes = prrfp->dwMediaTypes;
629             rgPins2[i].nMediums = prrfp->dwMediums;
630             pCurrent += sizeof(struct REG_RFP);
631             if (prrfp->bCategory)
632             {
633                 CLSID * clsCat = CoTaskMemAlloc(sizeof(CLSID));
634                 memcpy(clsCat, pData + *(DWORD*)(pCurrent), sizeof(CLSID));
635                 pCurrent += sizeof(DWORD);
636                 rgPins2[i].clsPinCategory = clsCat;
637             }
638             else
639                 rgPins2[i].clsPinCategory = NULL;
640
641             if (rgPins2[i].nMediaTypes > 0)
642                 lpMediaType = CoTaskMemAlloc(rgPins2[i].nMediaTypes * sizeof(*lpMediaType));
643             else
644                 lpMediaType = NULL;
645
646             rgPins2[i].lpMediaType = lpMediaType;
647
648             for (j = 0; j < rgPins2[i].nMediaTypes; j++)
649             {
650                 struct REG_TYPE * prt = (struct REG_TYPE *)pCurrent;
651                 CLSID * clsMajor = CoTaskMemAlloc(sizeof(CLSID));
652                 CLSID * clsMinor = CoTaskMemAlloc(sizeof(CLSID));
653
654                 /* FIXME: check signature */
655                 TRACE("\t\tsignature = %s\n", debugstr_an((const char*)prt->signature, 4));
656
657                 memcpy(clsMajor, pData + prt->dwOffsetMajor, sizeof(CLSID));
658                 memcpy(clsMinor, pData + prt->dwOffsetMinor, sizeof(CLSID));
659
660                 lpMediaType[j].clsMajorType = clsMajor;
661                 lpMediaType[j].clsMinorType = clsMinor;
662
663                 pCurrent += sizeof(*prt);
664             }
665
666             if (rgPins2[i].nMediums > 0)
667                 lpMedium = CoTaskMemAlloc(rgPins2[i].nMediums * sizeof(*lpMedium));
668             else
669                 lpMedium = NULL;
670
671             rgPins2[i].lpMedium = lpMedium;
672
673             for (j = 0; j < rgPins2[i].nMediums; j++)
674             {
675                 DWORD dwOffset = *(DWORD *)pCurrent;
676
677                 memcpy(lpMedium + j, pData + dwOffset, sizeof(REGPINMEDIUM));
678
679                 pCurrent += sizeof(dwOffset);
680             }
681         }
682
683     }
684
685     return hr;
686 }
687
688 static void FM2_DeleteRegFilter(REGFILTER2 * prf2)
689 {
690     UINT i;
691     for (i = 0; i < prf2->u.s2.cPins2; i++)
692     {
693         UINT j;
694         if (prf2->u.s2.rgPins2[i].clsPinCategory)
695             CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2[i].clsPinCategory);
696
697         for (j = 0; j < prf2->u.s2.rgPins2[i].nMediaTypes; j++)
698         {
699             CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2[i].lpMediaType[j].clsMajorType);
700             CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2[i].lpMediaType[j].clsMinorType);
701         }
702         CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2[i].lpMediaType);
703         CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2[i].lpMedium);
704     }
705     CoTaskMemFree((LPVOID)prf2->u.s2.rgPins2);
706 }
707
708 static HRESULT WINAPI FilterMapper3_RegisterFilter(
709     IFilterMapper3 * iface,
710     REFCLSID clsidFilter,
711     LPCWSTR szName,
712     IMoniker **ppMoniker,
713     const CLSID *pclsidCategory,
714     const OLECHAR *szInstance,
715     const REGFILTER2 *prf2)
716 {
717     IParseDisplayName * pParser = NULL;
718     IBindCtx * pBindCtx = NULL;
719     IMoniker * pMoniker = NULL;
720     IPropertyBag * pPropBag = NULL;
721     HRESULT hr;
722     LPWSTR pwszParseName = NULL;
723     LPWSTR pCurrent;
724     static const WCHAR wszDevice[] = {'@','d','e','v','i','c','e',':','s','w',':',0};
725     int nameLen;
726     ULONG ulEaten;
727     LPWSTR szClsidTemp = NULL;
728     REGFILTER2 regfilter2;
729     REGFILTERPINS2* pregfp2 = NULL;
730
731     TRACE("(%s, %s, %p, %s, %s, %p)\n",
732         debugstr_guid(clsidFilter),
733         debugstr_w(szName),
734         ppMoniker,
735         debugstr_guid(pclsidCategory),
736         debugstr_w(szInstance),
737         prf2);
738
739     if (prf2->dwVersion == 2)
740     {
741         regfilter2 = *prf2;
742     }
743     else if (prf2->dwVersion == 1)
744     {
745         ULONG i;
746         DWORD flags;
747         /* REGFILTER2 structure is converted from version 1 to 2. Tested on Win2k. */
748         regfilter2.dwVersion = 2;
749         regfilter2.dwMerit = prf2->dwMerit;
750         regfilter2.u.s2.cPins2 = prf2->u.s1.cPins;
751         pregfp2 = CoTaskMemAlloc(prf2->u.s1.cPins * sizeof(REGFILTERPINS2));
752         regfilter2.u.s2.rgPins2 = pregfp2;
753         for (i = 0; i < prf2->u.s1.cPins; i++)
754         {
755             flags = 0;
756             if (prf2->u.s1.rgPins[i].bRendered)
757                 flags |= REG_PINFLAG_B_RENDERER;
758             if (prf2->u.s1.rgPins[i].bOutput)
759                 flags |= REG_PINFLAG_B_OUTPUT;
760             if (prf2->u.s1.rgPins[i].bZero)
761                 flags |= REG_PINFLAG_B_ZERO;
762             if (prf2->u.s1.rgPins[i].bMany)
763                 flags |= REG_PINFLAG_B_MANY;
764             pregfp2[i].dwFlags = flags;
765             pregfp2[i].cInstances = 1;
766             pregfp2[i].nMediaTypes = prf2->u.s1.rgPins[i].nMediaTypes;
767             pregfp2[i].lpMediaType = prf2->u.s1.rgPins[i].lpMediaType;
768             pregfp2[i].nMediums = 0;
769             pregfp2[i].lpMedium = NULL;
770             pregfp2[i].clsPinCategory = NULL;
771         }
772     }
773     else
774     {
775         FIXME("dwVersion other that 1 or 2 not supported at the moment\n");
776         return E_NOTIMPL;
777     }
778
779     if (ppMoniker)
780         *ppMoniker = NULL;
781
782     if (!pclsidCategory)
783         /* MSDN mentions the inexistent CLSID_ActiveMovieFilters GUID.
784          * In fact this is the CLSID_LegacyAmFilterCategory one */
785         pclsidCategory = &CLSID_LegacyAmFilterCategory;
786
787     /* sizeof... will include the null terminator and
788      * the + 1 is for the separator ('\\'). The -1 is
789      * because CHARS_IN_GUID includes the null terminator
790      */
791     nameLen = sizeof(wszDevice)/sizeof(wszDevice[0]) + CHARS_IN_GUID - 1 + 1;
792
793     if (szInstance)
794         nameLen += strlenW(szInstance);
795     else
796         nameLen += CHARS_IN_GUID - 1; /* CHARS_IN_GUID includes null terminator */
797
798     pCurrent = pwszParseName = CoTaskMemAlloc(nameLen*sizeof(WCHAR));
799     if (!pwszParseName)
800         return E_OUTOFMEMORY;
801
802     strcpyW(pwszParseName, wszDevice);
803     pCurrent += strlenW(wszDevice);
804
805     hr = StringFromCLSID(pclsidCategory, &szClsidTemp);
806
807     if (SUCCEEDED(hr))
808     {
809         memcpy(pCurrent, szClsidTemp, CHARS_IN_GUID * sizeof(WCHAR));
810         pCurrent += CHARS_IN_GUID - 1;
811         pCurrent[0] = '\\';
812
813         if (szInstance)
814             strcpyW(pCurrent+1, szInstance);
815         else
816         {
817             CoTaskMemFree(szClsidTemp);
818             szClsidTemp = NULL;
819
820             hr = StringFromCLSID(clsidFilter, &szClsidTemp);
821             if (SUCCEEDED(hr))
822                 strcpyW(pCurrent+1, szClsidTemp);
823         }
824     }
825
826     if (SUCCEEDED(hr))
827         hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (LPVOID *)&pParser);
828
829     if (SUCCEEDED(hr))
830         hr = CreateBindCtx(0, &pBindCtx);
831
832     if (SUCCEEDED(hr))
833         hr = IParseDisplayName_ParseDisplayName(pParser, pBindCtx, pwszParseName, &ulEaten, &pMoniker);
834
835     if (pBindCtx)
836         IBindCtx_Release(pBindCtx);
837     if (pParser)
838         IParseDisplayName_Release(pParser);
839
840     if (SUCCEEDED(hr))
841         hr = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag);
842
843     if (SUCCEEDED(hr))
844         hr = FM2_WriteFriendlyName(pPropBag, szName);
845
846     if (SUCCEEDED(hr))
847         hr = FM2_WriteClsid(pPropBag, clsidFilter);
848
849     if (SUCCEEDED(hr))
850     {
851         BYTE *pData;
852         ULONG cbData;
853
854         hr = FM2_WriteFilterData(&regfilter2, &pData, &cbData);
855         if (SUCCEEDED(hr))
856         {
857             VARIANT var;
858             SAFEARRAY *psa;
859             SAFEARRAYBOUND saBound;
860
861             saBound.lLbound = 0;
862             saBound.cElements = cbData;
863             psa = SafeArrayCreate(VT_UI1, 1, &saBound);
864             if (!psa)
865             {
866                 ERR("Couldn't create SAFEARRAY\n");
867                 hr = E_FAIL;
868             }
869
870             if (SUCCEEDED(hr))
871             {
872                 LPBYTE pbSAData;
873                 hr = SafeArrayAccessData(psa, (LPVOID *)&pbSAData);
874                 if (SUCCEEDED(hr))
875                 {
876                     memcpy(pbSAData, pData, cbData);
877                     hr = SafeArrayUnaccessData(psa);
878                 }
879             }
880
881             V_VT(&var) = VT_ARRAY | VT_UI1;
882             V_UNION(&var, parray) = psa;
883
884             if (SUCCEEDED(hr))
885                 hr = IPropertyBag_Write(pPropBag, wszFilterDataName, &var);
886
887             if (psa)
888                 SafeArrayDestroy(psa);
889             CoTaskMemFree(pData);
890         }
891     }
892
893     if (pPropBag)
894         IPropertyBag_Release(pPropBag);
895     CoTaskMemFree(szClsidTemp);
896     CoTaskMemFree(pwszParseName);
897
898     if (SUCCEEDED(hr) && ppMoniker)
899         *ppMoniker = pMoniker;
900     else if (pMoniker)
901         IMoniker_Release(pMoniker);
902
903     CoTaskMemFree(pregfp2);
904
905     TRACE("-- returning %x\n", hr);
906
907     return hr;
908 }
909
910 /* internal helper function */
911 static BOOL MatchTypes(
912     BOOL bExactMatch,
913     DWORD nPinTypes,
914     const REGPINTYPES * pPinTypes,
915     DWORD nMatchTypes,
916     const GUID * pMatchTypes)
917 {
918     BOOL bMatch = FALSE;
919     DWORD j;
920
921     if ((nMatchTypes == 0) && (nPinTypes > 0))
922         bMatch = TRUE;
923
924     for (j = 0; j < nPinTypes; j++)
925     {
926         DWORD i;
927         for (i = 0; i < nMatchTypes*2; i+=2)
928         {
929             if (((!bExactMatch && IsEqualGUID(pPinTypes[j].clsMajorType, &GUID_NULL)) || IsEqualGUID(&pMatchTypes[i], &GUID_NULL) || IsEqualGUID(pPinTypes[j].clsMajorType, &pMatchTypes[i])) &&
930                 ((!bExactMatch && IsEqualGUID(pPinTypes[j].clsMinorType, &GUID_NULL)) || IsEqualGUID(&pMatchTypes[i+1], &GUID_NULL) || IsEqualGUID(pPinTypes[j].clsMinorType, &pMatchTypes[i+1])))
931             {
932                 bMatch = TRUE;
933                 break;
934             }
935         }
936     }
937     return bMatch;
938 }
939
940 /* internal helper function for qsort of MONIKER_MERIT array */
941 static int mm_compare(const void * left, const void * right)
942 {
943     const struct MONIKER_MERIT * mmLeft = left;
944     const struct MONIKER_MERIT * mmRight = right;
945
946     if (mmLeft->dwMerit == mmRight->dwMerit)
947         return 0;
948     if (mmLeft->dwMerit > mmRight->dwMerit)
949         return -1;
950     return 1;
951 }
952
953 /* NOTES:
954  *   Exact match means whether or not to treat GUID_NULL's in filter data as wild cards
955  *    (GUID_NULL's in input to function automatically treated as wild cards)
956  *   Input/Output needed means match only on criteria if TRUE (with zero input types
957  *    meaning match any input/output pin as long as one exists), otherwise match any
958  *    filter that meets the rest of the requirements.
959  */
960 static HRESULT WINAPI FilterMapper3_EnumMatchingFilters(
961     IFilterMapper3 * iface,
962     IEnumMoniker **ppEnum,
963     DWORD dwFlags,
964     BOOL bExactMatch,
965     DWORD dwMerit,
966     BOOL bInputNeeded,
967     DWORD cInputTypes,
968     const GUID *pInputTypes,
969     const REGPINMEDIUM *pMedIn,
970     const CLSID *pPinCategoryIn,
971     BOOL bRender,
972     BOOL bOutputNeeded,
973     DWORD cOutputTypes,
974     const GUID *pOutputTypes,
975     const REGPINMEDIUM *pMedOut,
976     const CLSID *pPinCategoryOut)
977 {
978     ICreateDevEnum * pCreateDevEnum;
979     IMoniker * pMonikerCat;
980     IEnumMoniker * pEnumCat;
981     HRESULT hr;
982     struct Vector monikers = {NULL, 0, 0};
983
984     TRACE("(%p, %x, %s, %x, %s, %d, %p, %p, %p, %s, %s, %p, %p, %p)\n",
985         ppEnum,
986         dwFlags,
987         bExactMatch ? "true" : "false",
988         dwMerit,
989         bInputNeeded ? "true" : "false",
990         cInputTypes,
991         pInputTypes,
992         pMedIn,
993         pPinCategoryIn,
994         bRender ? "true" : "false",
995         bOutputNeeded ? "true" : "false",
996         pOutputTypes,
997         pMedOut,
998         pPinCategoryOut);
999
1000     if (dwFlags != 0)
1001     {
1002         FIXME("dwFlags = %x not implemented\n", dwFlags);
1003     }
1004
1005     *ppEnum = NULL;
1006
1007     hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, &IID_ICreateDevEnum, (LPVOID*)&pCreateDevEnum);
1008     if (FAILED(hr))
1009         return hr;
1010
1011     hr = ICreateDevEnum_CreateClassEnumerator(pCreateDevEnum, &CLSID_ActiveMovieCategories, &pEnumCat, 0);
1012     if (FAILED(hr)) {
1013         ICreateDevEnum_Release(pCreateDevEnum);
1014         return hr;
1015     }
1016
1017     while (IEnumMoniker_Next(pEnumCat, 1, &pMonikerCat, NULL) == S_OK)
1018     {
1019         IPropertyBag * pPropBagCat = NULL;
1020         VARIANT var;
1021         HRESULT hrSub; /* this is so that one buggy filter
1022                           doesn't make the whole lot fail */
1023
1024         VariantInit(&var);
1025
1026         hrSub = IMoniker_BindToStorage(pMonikerCat, NULL, NULL, &IID_IPropertyBag, (LPVOID*)&pPropBagCat);
1027
1028         if (SUCCEEDED(hrSub))
1029             hrSub = IPropertyBag_Read(pPropBagCat, wszMeritName, &var, NULL);
1030
1031         if (SUCCEEDED(hrSub) && (V_UNION(&var, ulVal) >= dwMerit))
1032         {
1033             CLSID clsidCat;
1034             IEnumMoniker * pEnum;
1035             IMoniker * pMoniker;
1036
1037             VariantClear(&var);
1038
1039             if (TRACE_ON(quartz))
1040             {
1041                 VARIANT temp;
1042                 V_VT(&temp) = VT_EMPTY;
1043                 IPropertyBag_Read(pPropBagCat, wszFriendlyName, &temp, NULL);
1044                 TRACE("Considering category %s\n", debugstr_w(V_UNION(&temp, bstrVal)));
1045                 VariantClear(&temp);
1046             }
1047
1048             hrSub = IPropertyBag_Read(pPropBagCat, wszClsidName, &var, NULL);
1049
1050             if (SUCCEEDED(hrSub))
1051                 hrSub = CLSIDFromString(V_UNION(&var, bstrVal), &clsidCat);
1052
1053             if (SUCCEEDED(hrSub))
1054                 hrSub = ICreateDevEnum_CreateClassEnumerator(pCreateDevEnum, &clsidCat, &pEnum, 0);
1055
1056             if (hrSub == S_OK)
1057             {
1058                 while (IEnumMoniker_Next(pEnum, 1, &pMoniker, NULL) == S_OK)
1059                 {
1060                     IPropertyBag * pPropBag = NULL;
1061                     VARIANT var;
1062                     BYTE *pData = NULL;
1063                     REGFILTER2 rf2;
1064                     DWORD i;
1065                     BOOL bInputMatch = !bInputNeeded;
1066                     BOOL bOutputMatch = !bOutputNeeded;
1067
1068                     ZeroMemory(&rf2, sizeof(rf2));
1069                     VariantInit(&var);
1070
1071                     hrSub = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID*)&pPropBag);
1072
1073                     if (TRACE_ON(quartz))
1074                     {
1075                         VARIANT temp;
1076                         V_VT(&temp) = VT_EMPTY;
1077                         IPropertyBag_Read(pPropBag, wszFriendlyName, &temp, NULL);
1078                         TRACE("Considering filter %s\n", debugstr_w(V_UNION(&temp, bstrVal)));
1079                         VariantClear(&temp);
1080                     }
1081
1082                     if (SUCCEEDED(hrSub))
1083                     {
1084                         hrSub = IPropertyBag_Read(pPropBag, wszFilterDataName, &var, NULL);
1085                     }
1086
1087                     if (SUCCEEDED(hrSub))
1088                         hrSub = SafeArrayAccessData(V_UNION(&var, parray), (LPVOID*)&pData);
1089
1090                     if (SUCCEEDED(hrSub))
1091                         hrSub = FM2_ReadFilterData(pData, &rf2);
1092
1093                     if (pData)
1094                         SafeArrayUnaccessData(V_UNION(&var, parray));
1095
1096                     VariantClear(&var);
1097
1098                     /* Logic used for bInputMatch expression:
1099                      * There exists some pin such that bInputNeeded implies (pin is an input and
1100                      * (bRender implies pin has render flag) and major/minor types members of
1101                      * pInputTypes )
1102                      * bOutputMatch is similar, but without the "bRender implies ..." part
1103                      * and substituting variables names containing input for output
1104                      */
1105
1106                     /* determine whether filter meets requirements */
1107                     if (SUCCEEDED(hrSub) && (rf2.dwMerit >= dwMerit))
1108                     {
1109                         for (i = 0; (i < rf2.u.s2.cPins2) && (!bInputMatch || !bOutputMatch); i++)
1110                         {
1111                             const REGFILTERPINS2 * rfp2 = rf2.u.s2.rgPins2 + i;
1112
1113                             bInputMatch = bInputMatch || (!(rfp2->dwFlags & REG_PINFLAG_B_OUTPUT) &&
1114                                 (!bRender || (rfp2->dwFlags & REG_PINFLAG_B_RENDERER)) &&
1115                                 MatchTypes(bExactMatch, rfp2->nMediaTypes, rfp2->lpMediaType, cInputTypes, pInputTypes));
1116                             bOutputMatch = bOutputMatch || ((rfp2->dwFlags & REG_PINFLAG_B_OUTPUT) &&
1117                                 MatchTypes(bExactMatch, rfp2->nMediaTypes, rfp2->lpMediaType, cOutputTypes, pOutputTypes));
1118                         }
1119
1120                         if (bInputMatch && bOutputMatch)
1121                         {
1122                             struct MONIKER_MERIT mm = {pMoniker, rf2.dwMerit};
1123                             IMoniker_AddRef(pMoniker);
1124                             add_data(&monikers, (LPBYTE)&mm, sizeof(mm));
1125                         }
1126                     }
1127
1128                     FM2_DeleteRegFilter(&rf2);
1129                     if (pPropBag)
1130                         IPropertyBag_Release(pPropBag);
1131                     IMoniker_Release(pMoniker);
1132                 }
1133                 IEnumMoniker_Release(pEnum);
1134             }
1135         }
1136
1137         VariantClear(&var);
1138         if (pPropBagCat)
1139             IPropertyBag_Release(pPropBagCat);
1140         IMoniker_Release(pMonikerCat);
1141     }
1142
1143     if (SUCCEEDED(hr))
1144     {
1145         IMoniker ** ppMoniker;
1146         unsigned int i;
1147         ULONG nMonikerCount = monikers.current / sizeof(struct MONIKER_MERIT);
1148
1149         /* sort the monikers in descending merit order */
1150         qsort(monikers.pData, nMonikerCount,
1151               sizeof(struct MONIKER_MERIT),
1152               mm_compare);
1153
1154         /* construct an IEnumMoniker interface */
1155         ppMoniker = CoTaskMemAlloc(nMonikerCount * sizeof(IMoniker *));
1156         for (i = 0; i < nMonikerCount; i++)
1157         {
1158             /* no need to AddRef here as already AddRef'd above */
1159             ppMoniker[i] = ((struct MONIKER_MERIT *)monikers.pData)[i].pMoniker;
1160         }
1161         hr = EnumMonikerImpl_Create(ppMoniker, nMonikerCount, ppEnum);
1162         CoTaskMemFree(ppMoniker);
1163     }
1164
1165     delete_vector(&monikers);
1166     IEnumMoniker_Release(pEnumCat);
1167     ICreateDevEnum_Release(pCreateDevEnum);
1168
1169     return hr;
1170 }
1171
1172 static HRESULT WINAPI FilterMapper3_GetICreateDevEnum(IFilterMapper3 *iface, ICreateDevEnum **ppEnum)
1173 {
1174     TRACE("(%p, %p)\n", iface, ppEnum);
1175     if (!ppEnum)
1176         return E_POINTER;
1177     return CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, &IID_ICreateDevEnum, (void**)ppEnum);
1178 }
1179
1180 static const IFilterMapper3Vtbl fm3vtbl =
1181 {
1182
1183     FilterMapper3_QueryInterface,
1184     FilterMapper3_AddRef,
1185     FilterMapper3_Release,
1186
1187     FilterMapper3_CreateCategory,
1188     FilterMapper3_UnregisterFilter,
1189     FilterMapper3_RegisterFilter,
1190     FilterMapper3_EnumMatchingFilters,
1191     FilterMapper3_GetICreateDevEnum
1192 };
1193
1194 /*** IUnknown methods ***/
1195
1196 static HRESULT WINAPI FilterMapper_QueryInterface(IFilterMapper * iface, REFIID riid, LPVOID *ppv)
1197 {
1198     FilterMapper3Impl *This = impl_from_IFilterMapper(iface);
1199
1200     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1201
1202     return FilterMapper3_QueryInterface(&This->IFilterMapper3_iface, riid, ppv);
1203 }
1204
1205 static ULONG WINAPI FilterMapper_AddRef(IFilterMapper * iface)
1206 {
1207     FilterMapper3Impl *This = impl_from_IFilterMapper(iface);
1208
1209     return FilterMapper3_AddRef(&This->IFilterMapper3_iface);
1210 }
1211
1212 static ULONG WINAPI FilterMapper_Release(IFilterMapper * iface)
1213 {
1214     FilterMapper3Impl *This = impl_from_IFilterMapper(iface);
1215
1216     return FilterMapper3_Release(&This->IFilterMapper3_iface);
1217 }
1218
1219 /*** IFilterMapper methods ***/
1220
1221 static HRESULT WINAPI FilterMapper_EnumMatchingFilters(
1222     IFilterMapper * iface,
1223     IEnumRegFilters **ppEnum,
1224     DWORD dwMerit,
1225     BOOL bInputNeeded,
1226     CLSID clsInMaj,
1227     CLSID clsInSub,
1228     BOOL bRender,
1229     BOOL bOutputNeeded,
1230     CLSID clsOutMaj,
1231     CLSID clsOutSub)
1232 {
1233     FilterMapper3Impl *This = impl_from_IFilterMapper(iface);
1234     GUID InputType[2];
1235     GUID OutputType[2];
1236     IEnumMoniker* ppEnumMoniker;
1237     IMoniker* IMon;
1238     ULONG nb;
1239     ULONG idx = 0, nb_mon = 0;
1240     REGFILTER* regfilters;
1241     HRESULT hr;
1242
1243     TRACE("(%p/%p)->(%p, %x, %s, %s, %s, %s, %s, %s, %s) stub!\n",
1244         iface,This,
1245         ppEnum,
1246         dwMerit,
1247         bInputNeeded ? "true" : "false",
1248         debugstr_guid(&clsInMaj),
1249         debugstr_guid(&clsInSub),
1250         bRender ? "true" : "false",
1251         bOutputNeeded ? "true" : "false",
1252         debugstr_guid(&clsOutMaj),
1253         debugstr_guid(&clsOutSub));
1254
1255     InputType[0] = clsInMaj;
1256     InputType[1] = clsInSub;
1257     OutputType[0] = clsOutMaj;
1258     OutputType[1] = clsOutSub;
1259
1260     *ppEnum = NULL;
1261
1262     hr = IFilterMapper3_EnumMatchingFilters(&This->IFilterMapper3_iface, &ppEnumMoniker, 0, TRUE,
1263             dwMerit, bInputNeeded, 1, InputType, NULL, &GUID_NULL, bRender, bOutputNeeded, 1,
1264             OutputType, NULL, &GUID_NULL);
1265
1266     if (FAILED(hr))
1267         return hr;
1268     
1269     while(IEnumMoniker_Next(ppEnumMoniker, 1, &IMon, &nb) == S_OK)
1270     {
1271         IMoniker_Release(IMon);
1272         nb_mon++;
1273     }
1274
1275     if (!nb_mon)
1276     {
1277         IEnumMoniker_Release(ppEnumMoniker);
1278         return IEnumRegFiltersImpl_Construct(NULL, 0, ppEnum);
1279     }
1280
1281     regfilters = CoTaskMemAlloc(nb_mon * sizeof(REGFILTER));
1282     if (!regfilters)
1283     {
1284         IEnumMoniker_Release(ppEnumMoniker);
1285         return E_OUTOFMEMORY;
1286     }
1287     ZeroMemory(regfilters, nb_mon * sizeof(REGFILTER)); /* will prevent bad free of Name in case of error. */
1288     
1289     IEnumMoniker_Reset(ppEnumMoniker);
1290     while(IEnumMoniker_Next(ppEnumMoniker, 1, &IMon, &nb) == S_OK)
1291     {
1292         IPropertyBag * pPropBagCat = NULL;
1293         VARIANT var;
1294         HRESULT hrSub;
1295         GUID clsid;
1296         int len;
1297
1298         VariantInit(&var);
1299
1300         hrSub = IMoniker_BindToStorage(IMon, NULL, NULL, &IID_IPropertyBag, (LPVOID*)&pPropBagCat);
1301
1302         if (SUCCEEDED(hrSub))
1303             hrSub = IPropertyBag_Read(pPropBagCat, wszClsidName, &var, NULL);
1304
1305         if (SUCCEEDED(hrSub))
1306             hrSub = CLSIDFromString(V_UNION(&var, bstrVal), &clsid);
1307
1308         VariantClear(&var);
1309
1310         if (SUCCEEDED(hrSub))
1311             hrSub = IPropertyBag_Read(pPropBagCat, wszFriendlyName, &var, NULL);
1312
1313         if (SUCCEEDED(hrSub))
1314         {
1315             len = (strlenW(V_UNION(&var, bstrVal))+1) * sizeof(WCHAR);
1316             if (!(regfilters[idx].Name = CoTaskMemAlloc(len*2)))
1317                 hr = E_OUTOFMEMORY;
1318         }
1319
1320         if (SUCCEEDED(hrSub) && regfilters[idx].Name)
1321         {
1322             memcpy(regfilters[idx].Name, V_UNION(&var, bstrVal), len);
1323             regfilters[idx].Clsid = clsid;
1324             idx++;
1325         }
1326
1327         if (pPropBagCat)
1328             IPropertyBag_Release(pPropBagCat);
1329         IMoniker_Release(IMon);
1330         VariantClear(&var);
1331     }
1332
1333     if (SUCCEEDED(hr))
1334     {
1335         hr = IEnumRegFiltersImpl_Construct(regfilters, nb_mon, ppEnum);
1336     }
1337
1338     for (idx = 0; idx < nb_mon; idx++)
1339         CoTaskMemFree(regfilters[idx].Name);
1340     CoTaskMemFree(regfilters);
1341     IEnumMoniker_Release(ppEnumMoniker);
1342     
1343     return hr;
1344 }
1345
1346
1347 static HRESULT WINAPI FilterMapper_RegisterFilter(IFilterMapper * iface, CLSID clsid, LPCWSTR szName, DWORD dwMerit)
1348 {
1349     HRESULT hr;
1350     LPWSTR wszClsid = NULL;
1351     HKEY hKey;
1352     LONG lRet;
1353     WCHAR wszKeyName[ARRAYSIZE(wszFilterSlash)-1 + (CHARS_IN_GUID-1) + 1];
1354
1355     TRACE("(%p)->(%s, %s, %x)\n", iface, debugstr_guid(&clsid), debugstr_w(szName), dwMerit);
1356
1357     hr = StringFromCLSID(&clsid, &wszClsid);
1358
1359     if (SUCCEEDED(hr))
1360     {
1361         strcpyW(wszKeyName, wszFilterSlash);
1362         strcatW(wszKeyName, wszClsid);
1363     
1364         lRet = RegCreateKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
1365         hr = HRESULT_FROM_WIN32(lRet);
1366     }
1367
1368     if (SUCCEEDED(hr))
1369     {
1370         lRet = RegSetValueExW(hKey, NULL, 0, REG_SZ, (const BYTE*)szName, (strlenW(szName) + 1) * sizeof(WCHAR));
1371         hr = HRESULT_FROM_WIN32(lRet);
1372         CloseHandle(hKey);
1373     }
1374
1375     if (SUCCEEDED(hr))
1376     {
1377         strcpyW(wszKeyName, wszClsidSlash);
1378         strcatW(wszKeyName, wszClsid);
1379     
1380         lRet = RegCreateKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL);
1381         hr = HRESULT_FROM_WIN32(lRet);
1382     }
1383
1384     if (SUCCEEDED(hr))
1385     {
1386         lRet = RegSetValueExW(hKey, wszMeritName, 0, REG_DWORD, (LPBYTE)&dwMerit, sizeof(dwMerit));
1387         hr = HRESULT_FROM_WIN32(lRet);
1388         CloseHandle(hKey);
1389     }
1390     
1391     CoTaskMemFree(wszClsid);
1392
1393     return hr;
1394 }
1395
1396 static HRESULT WINAPI FilterMapper_RegisterFilterInstance(IFilterMapper * iface, CLSID clsid, LPCWSTR szName, CLSID *MRId)
1397 {
1398     TRACE("(%p)->(%s, %s, %p)\n", iface, debugstr_guid(&clsid), debugstr_w(szName), MRId);
1399
1400     /* Not implemented in Windows (tested on Win2k) */
1401
1402     return E_NOTIMPL;
1403 }
1404
1405 static HRESULT WINAPI FilterMapper_RegisterPin(
1406     IFilterMapper * iface,
1407     CLSID Filter,
1408     LPCWSTR szName,
1409     BOOL bRendered,
1410     BOOL bOutput,
1411     BOOL bZero,
1412     BOOL bMany,
1413     CLSID ConnectsToFilter,
1414     LPCWSTR ConnectsToPin)
1415 {
1416     HRESULT hr;
1417     LONG lRet;
1418     LPWSTR wszClsid = NULL;
1419     HKEY hKey = NULL;
1420     HKEY hPinsKey = NULL;
1421     WCHAR * wszPinsKeyName;
1422     WCHAR wszKeyName[ARRAYSIZE(wszClsidSlash)-1 + (CHARS_IN_GUID-1) + 1];
1423
1424     TRACE("(%p)->(%s, %s, %d, %d, %d, %d, %s, %s)\n", iface, debugstr_guid(&Filter), debugstr_w(szName), bRendered,
1425                 bOutput, bZero, bMany, debugstr_guid(&ConnectsToFilter), debugstr_w(ConnectsToPin));
1426
1427     hr = StringFromCLSID(&Filter, &wszClsid);
1428
1429     if (SUCCEEDED(hr))
1430     {
1431         strcpyW(wszKeyName, wszClsidSlash);
1432         strcatW(wszKeyName, wszClsid);
1433
1434         lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, KEY_WRITE, &hKey);
1435         hr = HRESULT_FROM_WIN32(lRet);
1436     }
1437
1438     if (SUCCEEDED(hr))
1439     {
1440         wszPinsKeyName = CoTaskMemAlloc((strlenW(wszPins) + 1 + strlenW(szName) + 1) * 2);
1441         if (!wszPinsKeyName)
1442              hr = E_OUTOFMEMORY;
1443     }
1444
1445     if (SUCCEEDED(hr))
1446     {
1447         strcpyW(wszPinsKeyName, wszPins);
1448         strcatW(wszPinsKeyName, wszSlash);
1449         strcatW(wszPinsKeyName, szName);
1450     
1451         lRet = RegCreateKeyExW(hKey, wszPinsKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hPinsKey, NULL);
1452         hr = HRESULT_FROM_WIN32(lRet);
1453         CoTaskMemFree(wszPinsKeyName);
1454     }
1455
1456     if (SUCCEEDED(hr))
1457     {
1458         lRet = RegSetValueExW(hPinsKey, wszAllowedMany, 0, REG_DWORD, (LPBYTE)&bMany, sizeof(bMany));
1459         hr = HRESULT_FROM_WIN32(lRet);
1460     }
1461
1462     if (SUCCEEDED(hr))
1463     {
1464         lRet = RegSetValueExW(hPinsKey, wszAllowedZero, 0, REG_DWORD, (LPBYTE)&bZero, sizeof(bZero));
1465         hr = HRESULT_FROM_WIN32(lRet);
1466     }
1467
1468     if (SUCCEEDED(hr))
1469     {
1470         lRet = RegSetValueExW(hPinsKey, wszDirection, 0, REG_DWORD, (LPBYTE)&bOutput, sizeof(bOutput));
1471         hr = HRESULT_FROM_WIN32(lRet);
1472     }
1473
1474     if (SUCCEEDED(hr))
1475     {
1476         lRet = RegSetValueExW(hPinsKey, wszIsRendered, 0, REG_DWORD, (LPBYTE)&bRendered, sizeof(bRendered));
1477         hr = HRESULT_FROM_WIN32(lRet);
1478     }
1479
1480     if (SUCCEEDED(hr))
1481     {
1482         HKEY hkeyDummy = NULL;
1483
1484         lRet = RegCreateKeyExW(hPinsKey, wszTypes, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyDummy, NULL);
1485         hr = HRESULT_FROM_WIN32(lRet);
1486
1487         if (hkeyDummy) RegCloseKey(hkeyDummy);
1488     }
1489
1490     CoTaskMemFree(wszClsid);
1491     if (hKey)
1492         CloseHandle(hKey);
1493     if (hPinsKey)
1494         CloseHandle(hPinsKey);
1495
1496     return hr;
1497 }
1498
1499
1500 static HRESULT WINAPI FilterMapper_RegisterPinType(
1501     IFilterMapper * iface,
1502     CLSID clsFilter,
1503     LPCWSTR szName,
1504     CLSID clsMajorType,
1505     CLSID clsSubType)
1506 {
1507     HRESULT hr;
1508     LONG lRet;
1509     LPWSTR wszClsid = NULL;
1510     LPWSTR wszClsidMajorType = NULL;
1511     LPWSTR wszClsidSubType = NULL;
1512     HKEY hKey = NULL;
1513     WCHAR * wszTypesKey;
1514     WCHAR wszKeyName[MAX_PATH];
1515
1516     TRACE("(%p)->(%s, %s, %s, %s)\n", iface, debugstr_guid(&clsFilter), debugstr_w(szName),
1517                     debugstr_guid(&clsMajorType), debugstr_guid(&clsSubType));
1518
1519     hr = StringFromCLSID(&clsFilter, &wszClsid);
1520
1521     if (SUCCEEDED(hr))
1522     {
1523         hr = StringFromCLSID(&clsMajorType, &wszClsidMajorType);
1524     }
1525
1526     if (SUCCEEDED(hr))
1527     {
1528         hr = StringFromCLSID(&clsSubType, &wszClsidSubType);
1529     }
1530
1531     if (SUCCEEDED(hr))
1532     {
1533         wszTypesKey = CoTaskMemAlloc((strlenW(wszClsidSlash) + strlenW(wszClsid) + strlenW(wszPins) +
1534                         strlenW(szName) + strlenW(wszTypes) + 3 + 1) * 2);
1535         if (!wszTypesKey)
1536             hr = E_OUTOFMEMORY;
1537     }
1538
1539     if (SUCCEEDED(hr))
1540     {
1541         strcpyW(wszTypesKey, wszClsidSlash);
1542         strcatW(wszTypesKey, wszClsid);
1543         strcatW(wszTypesKey, wszSlash);
1544         strcatW(wszTypesKey, wszPins);
1545         strcatW(wszTypesKey, wszSlash);
1546         strcatW(wszTypesKey, szName);
1547         strcatW(wszTypesKey, wszSlash);
1548         strcatW(wszTypesKey, wszTypes);
1549
1550         lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTypesKey, 0, KEY_WRITE, &hKey);
1551         hr = HRESULT_FROM_WIN32(lRet);
1552         CoTaskMemFree(wszTypesKey);
1553     }
1554
1555     if (SUCCEEDED(hr))
1556     {
1557         HKEY hkeyDummy = NULL;
1558
1559         strcpyW(wszKeyName, wszClsidMajorType);
1560         strcatW(wszKeyName, wszSlash);
1561         strcatW(wszKeyName, wszClsidSubType);
1562
1563         lRet = RegCreateKeyExW(hKey, wszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkeyDummy, NULL);
1564         hr = HRESULT_FROM_WIN32(lRet);
1565         CloseHandle(hKey);
1566
1567         if (hkeyDummy) RegCloseKey(hkeyDummy);
1568     }
1569
1570     CoTaskMemFree(wszClsid);
1571     CoTaskMemFree(wszClsidMajorType);
1572     CoTaskMemFree(wszClsidSubType);
1573
1574     return hr;
1575 }
1576
1577 static HRESULT WINAPI FilterMapper_UnregisterFilter(IFilterMapper * iface, CLSID Filter)
1578 {
1579     HRESULT hr;
1580     LONG lRet;
1581     LPWSTR wszClsid = NULL;
1582     HKEY hKey;
1583     WCHAR wszKeyName[ARRAYSIZE(wszClsidSlash)-1 + (CHARS_IN_GUID-1) + 1];
1584
1585     TRACE("(%p)->(%s)\n", iface, debugstr_guid(&Filter));
1586
1587     hr = StringFromCLSID(&Filter, &wszClsid);
1588
1589     if (SUCCEEDED(hr))
1590     {
1591         lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFilter, 0, KEY_WRITE, &hKey);
1592         hr = HRESULT_FROM_WIN32(lRet);
1593     }
1594
1595     if (SUCCEEDED(hr))
1596     {
1597         lRet = RegDeleteKeyW(hKey, wszClsid);
1598         hr = HRESULT_FROM_WIN32(lRet);
1599         CloseHandle(hKey);
1600     }
1601
1602     if (SUCCEEDED(hr))
1603     {
1604         strcpyW(wszKeyName, wszClsidSlash);
1605         strcatW(wszKeyName, wszClsid);
1606
1607         lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, KEY_WRITE, &hKey);
1608         hr = HRESULT_FROM_WIN32(lRet);
1609     }
1610
1611     if (SUCCEEDED(hr))
1612     {
1613         lRet = RegDeleteValueW(hKey, wszMeritName);
1614         if (lRet != ERROR_SUCCESS)
1615             hr = HRESULT_FROM_WIN32(lRet);
1616
1617         lRet = RegDeleteTreeW(hKey, wszPins);
1618         if (lRet != ERROR_SUCCESS)
1619             hr = HRESULT_FROM_WIN32(lRet);
1620
1621         CloseHandle(hKey);
1622     }
1623
1624     CoTaskMemFree(wszClsid);
1625
1626     return hr;
1627 }
1628
1629 static HRESULT WINAPI FilterMapper_UnregisterFilterInstance(IFilterMapper * iface, CLSID MRId)
1630 {
1631     TRACE("(%p)->(%s)\n", iface, debugstr_guid(&MRId));
1632
1633     /* Not implemented in Windows (tested on Win2k) */
1634
1635     return E_NOTIMPL;
1636 }
1637
1638 static HRESULT WINAPI FilterMapper_UnregisterPin(IFilterMapper * iface, CLSID Filter, LPCWSTR Name)
1639 {
1640     HRESULT hr;
1641     LONG lRet;
1642     LPWSTR wszClsid = NULL;
1643     HKEY hKey = NULL;
1644     WCHAR * wszPinNameKey;
1645     WCHAR wszKeyName[ARRAYSIZE(wszClsidSlash)-1 + (CHARS_IN_GUID-1) + 1];
1646
1647     TRACE("(%p)->(%s, %s)\n", iface, debugstr_guid(&Filter), debugstr_w(Name));
1648
1649     if (!Name)
1650         return E_INVALIDARG;
1651
1652     hr = StringFromCLSID(&Filter, &wszClsid);
1653
1654     if (SUCCEEDED(hr))
1655     {
1656         strcpyW(wszKeyName, wszClsidSlash);
1657         strcatW(wszKeyName, wszClsid);
1658
1659         lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszKeyName, 0, KEY_WRITE, &hKey);
1660         hr = HRESULT_FROM_WIN32(lRet);
1661     }
1662
1663     if (SUCCEEDED(hr))
1664     {
1665         wszPinNameKey = CoTaskMemAlloc((strlenW(wszPins) + 1 + strlenW(Name) + 1) * 2);
1666         if (!wszPinNameKey)
1667             hr = E_OUTOFMEMORY;
1668     }
1669
1670     if (SUCCEEDED(hr))
1671     {
1672         strcpyW(wszPinNameKey, wszPins);
1673         strcatW(wszPinNameKey, wszSlash);
1674         strcatW(wszPinNameKey, Name);
1675
1676         lRet = RegDeleteTreeW(hKey, wszPinNameKey);
1677         hr = HRESULT_FROM_WIN32(lRet);
1678         CoTaskMemFree(wszPinNameKey);
1679     }
1680
1681     CoTaskMemFree(wszClsid);
1682     if (hKey)
1683         CloseHandle(hKey);
1684
1685     return hr;
1686 }
1687
1688 static const IFilterMapperVtbl fmvtbl =
1689 {
1690
1691     FilterMapper_QueryInterface,
1692     FilterMapper_AddRef,
1693     FilterMapper_Release,
1694
1695     FilterMapper_RegisterFilter,
1696     FilterMapper_RegisterFilterInstance,
1697     FilterMapper_RegisterPin,
1698     FilterMapper_RegisterPinType,
1699     FilterMapper_UnregisterFilter,
1700     FilterMapper_UnregisterFilterInstance,
1701     FilterMapper_UnregisterPin,
1702     FilterMapper_EnumMatchingFilters
1703 };
1704
1705
1706 /*** IUnknown methods ***/
1707 static HRESULT WINAPI AMFilterData_QueryInterface(IAMFilterData * iface, REFIID riid, LPVOID *ppv)
1708 {
1709     FilterMapper3Impl *This = impl_from_IAMFilterData(iface);
1710
1711     return FilterMapper3_QueryInterface(&This->IFilterMapper3_iface, riid, ppv);
1712 }
1713
1714 static ULONG WINAPI AMFilterData_AddRef(IAMFilterData * iface)
1715 {
1716     FilterMapper3Impl *This = impl_from_IAMFilterData(iface);
1717
1718     return FilterMapper3_AddRef(&This->IFilterMapper3_iface);
1719 }
1720
1721 static ULONG WINAPI AMFilterData_Release(IAMFilterData * iface)
1722 {
1723     FilterMapper3Impl *This = impl_from_IAMFilterData(iface);
1724
1725     return FilterMapper3_Release(&This->IFilterMapper3_iface);
1726 }
1727
1728 /*** IAMFilterData methods ***/
1729 static HRESULT WINAPI AMFilterData_ParseFilterData(IAMFilterData* iface,
1730                                                    BYTE *pData, ULONG cb,
1731                                                    BYTE **ppRegFilter2)
1732 {
1733     FilterMapper3Impl *This = impl_from_IAMFilterData(iface);
1734     HRESULT hr = S_OK;
1735     static REGFILTER2 *prf2;
1736
1737     TRACE("(%p/%p)->(%p, %d, %p)\n", This, iface, pData, cb, ppRegFilter2);
1738
1739     prf2 = CoTaskMemAlloc(sizeof(*prf2));
1740     if (!prf2)
1741         return E_OUTOFMEMORY;
1742     *ppRegFilter2 = (BYTE *)&prf2;
1743
1744     hr = FM2_ReadFilterData(pData, prf2);
1745     if (FAILED(hr))
1746     {
1747         CoTaskMemFree(prf2);
1748         *ppRegFilter2 = NULL;
1749     }
1750
1751     return hr;
1752 }
1753
1754 static HRESULT WINAPI AMFilterData_CreateFilterData(IAMFilterData* iface,
1755                                                     REGFILTER2 *prf2,
1756                                                     BYTE **pRegFilterData,
1757                                                     ULONG *pcb)
1758 {
1759     FilterMapper3Impl *This = impl_from_IAMFilterData(iface);
1760
1761     TRACE("(%p/%p)->(%p, %p, %p)\n", This, iface, prf2, pRegFilterData, pcb);
1762
1763     return FM2_WriteFilterData(prf2, pRegFilterData, pcb);
1764 }
1765
1766 static const IAMFilterDataVtbl AMFilterDataVtbl = {
1767     AMFilterData_QueryInterface,
1768     AMFilterData_AddRef,
1769     AMFilterData_Release,
1770     AMFilterData_ParseFilterData,
1771     AMFilterData_CreateFilterData
1772 };