quartz: Get rid of code duplication and add a flush method.
[wine] / dlls / quartz / filesource.c
1 /*
2  * File Source Filter
3  *
4  * Copyright 2003 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include "quartz_private.h"
25
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "pin.h"
29 #include "uuids.h"
30 #include "vfwmsgs.h"
31 #include "winbase.h"
32 #include "winreg.h"
33 #include "shlwapi.h"
34 #include <assert.h>
35
36 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
37
38 static const WCHAR wszOutputPinName[] = { 'O','u','t','p','u','t',0 };
39
40 typedef struct AsyncReader
41 {
42     const IBaseFilterVtbl * lpVtbl;
43     const IFileSourceFilterVtbl * lpVtblFSF;
44
45     LONG refCount;
46     FILTER_INFO filterInfo;
47     FILTER_STATE state;
48     CRITICAL_SECTION csFilter;
49
50     IPin * pOutputPin;
51     LPOLESTR pszFileName;
52     AM_MEDIA_TYPE * pmt;
53 } AsyncReader;
54
55 static const IBaseFilterVtbl AsyncReader_Vtbl;
56 static const IFileSourceFilterVtbl FileSource_Vtbl;
57 static const IAsyncReaderVtbl FileAsyncReader_Vtbl;
58
59 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
60
61 static inline AsyncReader *impl_from_IFileSourceFilter( IFileSourceFilter *iface )
62 {
63     return (AsyncReader *)((char*)iface - FIELD_OFFSET(AsyncReader, lpVtblFSF));
64 }
65
66 static WCHAR const mediatype_name[11] = {
67     'M', 'e', 'd', 'i', 'a', ' ', 'T', 'y', 'p', 'e', 0 };
68 static WCHAR const subtype_name[8] = {
69     'S', 'u', 'b', 't', 'y', 'p', 'e', 0 };
70
71 static HRESULT process_extensions(HKEY hkeyExtensions, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
72 {
73     WCHAR *extension;
74     LONG l;
75     HKEY hsub;
76     WCHAR keying[39];
77     DWORD size;
78
79     if (!pszFileName)
80         return E_POINTER;
81
82     /* Get the part of the name that matters */
83     extension = PathFindExtensionW(pszFileName);
84     if (*extension != '.')
85         return E_FAIL;
86
87     l = RegOpenKeyExW(hkeyExtensions, extension, 0, KEY_READ, &hsub);
88     if (l)
89         return E_FAIL;
90
91     size = sizeof(keying);
92     l = RegQueryValueExW(hsub, mediatype_name, NULL, NULL, (LPBYTE)keying, &size);
93     if (!l)
94         CLSIDFromString(keying, majorType);
95
96     size = sizeof(keying);
97     if (!l)
98         l = RegQueryValueExW(hsub, subtype_name, NULL, NULL, (LPBYTE)keying, &size);
99     if (!l)
100         CLSIDFromString(keying, minorType);
101
102     RegCloseKey(hsub);
103
104     if (!l)
105         return S_OK;
106     return E_FAIL;
107 }
108
109 static unsigned char byte_from_hex_char(WCHAR wHex)
110 {
111     switch (tolowerW(wHex))
112     {
113     case '0':
114     case '1':
115     case '2':
116     case '3':
117     case '4':
118     case '5':
119     case '6':
120     case '7':
121     case '8':
122     case '9':
123         return (wHex - '0') & 0xf;
124     case 'a':
125     case 'b':
126     case 'c':
127     case 'd':
128     case 'e':
129     case 'f':
130         return (wHex - 'a' + 10) & 0xf;
131     default:
132         return 0;
133     }
134 }
135
136 static HRESULT process_pattern_string(LPCWSTR wszPatternString, IAsyncReader * pReader)
137 {
138     ULONG ulOffset;
139     ULONG ulBytes;
140     BYTE * pbMask;
141     BYTE * pbValue;
142     BYTE * pbFile;
143     HRESULT hr = S_OK;
144     ULONG strpos;
145
146     TRACE("\t\tPattern string: %s\n", debugstr_w(wszPatternString));
147     
148     /* format: "offset, bytestocompare, mask, value" */
149
150     ulOffset = strtolW(wszPatternString, NULL, 10);
151
152     if (!(wszPatternString = strchrW(wszPatternString, ',')))
153         return E_INVALIDARG;
154
155     wszPatternString++; /* skip ',' */
156
157     ulBytes = strtolW(wszPatternString, NULL, 10);
158
159     pbMask = HeapAlloc(GetProcessHeap(), 0, ulBytes);
160     pbValue = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBytes);
161     pbFile = HeapAlloc(GetProcessHeap(), 0, ulBytes);
162
163     /* default mask is match everything */
164     memset(pbMask, 0xFF, ulBytes);
165
166     if (!(wszPatternString = strchrW(wszPatternString, ',')))
167         hr = E_INVALIDARG;
168
169     wszPatternString++; /* skip ',' */
170
171     if (hr == S_OK)
172     {
173         for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++)
174             ;
175
176         for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
177         {
178             if ((strpos % 2) == 1) /* odd numbered position */
179                 pbMask[strpos / 2] |= byte_from_hex_char(*wszPatternString);
180             else
181                 pbMask[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
182         }
183
184         if (!(wszPatternString = strchrW(wszPatternString, ',')))
185             hr = E_INVALIDARG;
186     
187         wszPatternString++; /* skip ',' */
188     }
189
190     if (hr == S_OK)
191     {
192         for ( ; !isxdigitW(*wszPatternString) && (*wszPatternString != ','); wszPatternString++)
193             ;
194
195         for (strpos = 0; isxdigitW(*wszPatternString) && (strpos/2 < ulBytes); wszPatternString++, strpos++)
196         {
197             if ((strpos % 2) == 1) /* odd numbered position */
198                 pbValue[strpos / 2] |= byte_from_hex_char(*wszPatternString);
199             else
200                 pbValue[strpos / 2] = byte_from_hex_char(*wszPatternString) << 4;
201         }
202     }
203
204     if (hr == S_OK)
205         hr = IAsyncReader_SyncRead(pReader, ulOffset, ulBytes, pbFile);
206
207     if (hr == S_OK)
208     {
209         ULONG i;
210         for (i = 0; i < ulBytes; i++)
211             if ((pbFile[i] & pbMask[i]) != pbValue[i])
212             {
213                 hr = S_FALSE;
214                 break;
215             }
216     }
217
218     HeapFree(GetProcessHeap(), 0, pbMask);
219     HeapFree(GetProcessHeap(), 0, pbValue);
220     HeapFree(GetProcessHeap(), 0, pbFile);
221
222     /* if we encountered no errors with this string, and there is a following tuple, then we
223      * have to match that as well to succeed */
224     if ((hr == S_OK) && (wszPatternString = strchrW(wszPatternString, ',')))
225         return process_pattern_string(wszPatternString + 1, pReader);
226     else
227         return hr;
228 }
229
230 static HRESULT GetClassMediaFile(IAsyncReader * pReader, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
231 {
232     HKEY hkeyMediaType = NULL;
233     LONG lRet;
234     HRESULT hr = S_OK;
235     BOOL bFound = FALSE;
236     static const WCHAR wszMediaType[] = {'M','e','d','i','a',' ','T','y','p','e',0};
237
238     TRACE("(%p, %s, %p, %p)\n", pReader, debugstr_w(pszFileName), majorType, minorType);
239
240     *majorType = GUID_NULL;
241     *minorType = GUID_NULL;
242
243     lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMediaType, 0, KEY_READ, &hkeyMediaType);
244     hr = HRESULT_FROM_WIN32(lRet);
245
246     if (SUCCEEDED(hr))
247     {
248         DWORD indexMajor;
249
250         for (indexMajor = 0; !bFound; indexMajor++)
251         {
252             HKEY hkeyMajor;
253             WCHAR wszMajorKeyName[CHARS_IN_GUID];
254             DWORD dwKeyNameLength = sizeof(wszMajorKeyName) / sizeof(wszMajorKeyName[0]);
255             static const WCHAR wszExtensions[] = {'E','x','t','e','n','s','i','o','n','s',0};
256
257             if (RegEnumKeyExW(hkeyMediaType, indexMajor, wszMajorKeyName, &dwKeyNameLength, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
258                 break;
259             if (RegOpenKeyExW(hkeyMediaType, wszMajorKeyName, 0, KEY_READ, &hkeyMajor) != ERROR_SUCCESS)
260                 break;
261             TRACE("%s\n", debugstr_w(wszMajorKeyName));
262             if (!strcmpW(wszExtensions, wszMajorKeyName))
263             {
264                 if (process_extensions(hkeyMajor, pszFileName, majorType, minorType) == S_OK)
265                     bFound = TRUE;
266             }
267             else
268             {
269                 DWORD indexMinor;
270
271                 for (indexMinor = 0; !bFound; indexMinor++)
272                 {
273                     HKEY hkeyMinor;
274                     WCHAR wszMinorKeyName[CHARS_IN_GUID];
275                     DWORD dwMinorKeyNameLen = sizeof(wszMinorKeyName) / sizeof(wszMinorKeyName[0]);
276                     DWORD maxValueLen;
277                     DWORD indexValue;
278
279                     if (RegEnumKeyExW(hkeyMajor, indexMinor, wszMinorKeyName, &dwMinorKeyNameLen, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
280                         break;
281
282                     if (RegOpenKeyExW(hkeyMajor, wszMinorKeyName, 0, KEY_READ, &hkeyMinor) != ERROR_SUCCESS)
283                         break;
284
285                     TRACE("\t%s\n", debugstr_w(wszMinorKeyName));
286         
287                     if (RegQueryInfoKeyW(hkeyMinor, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &maxValueLen, NULL, NULL) != ERROR_SUCCESS)
288                         break;
289
290                     for (indexValue = 0; !bFound; indexValue++)
291                     {
292                         DWORD dwType;
293                         WCHAR wszValueName[14]; /* longest name we should encounter will be "Source Filter" */
294                         LPWSTR wszPatternString = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
295                         DWORD dwValueNameLen = sizeof(wszValueName) / sizeof(wszValueName[0]); /* remember this is in chars */
296                         DWORD dwDataLen = maxValueLen; /* remember this is in bytes */
297                         static const WCHAR wszSourceFilter[] = {'S','o','u','r','c','e',' ','F','i','l','t','e','r',0};
298                         LONG temp;
299
300                         if ((temp = RegEnumValueW(hkeyMinor, indexValue, wszValueName, &dwValueNameLen, NULL, &dwType, (LPBYTE)wszPatternString, &dwDataLen)) != ERROR_SUCCESS)
301                         {
302                             HeapFree(GetProcessHeap(), 0, wszPatternString);
303                             break;
304                         }
305
306                         /* if it is not the source filter value */
307                         if (strcmpW(wszValueName, wszSourceFilter))
308                         {
309                             if (process_pattern_string(wszPatternString, pReader) == S_OK)
310                             {
311                                 if (SUCCEEDED(CLSIDFromString(wszMajorKeyName, majorType)) &&
312                                     SUCCEEDED(CLSIDFromString(wszMinorKeyName, minorType)))
313                                     bFound = TRUE;
314                             }
315                         }
316                         HeapFree(GetProcessHeap(), 0, wszPatternString);
317                     }
318                     CloseHandle(hkeyMinor);
319                 }
320             }
321             CloseHandle(hkeyMajor);
322         }
323     }
324     CloseHandle(hkeyMediaType);
325
326     if (SUCCEEDED(hr) && !bFound)
327     {
328         ERR("Media class not found\n");
329         hr = E_FAIL;
330     }
331     else if (bFound)
332         TRACE("Found file's class: major = %s, subtype = %s\n", qzdebugstr_guid(majorType), qzdebugstr_guid(minorType));
333
334     return hr;
335 }
336
337 HRESULT AsyncReader_create(IUnknown * pUnkOuter, LPVOID * ppv)
338 {
339     AsyncReader *pAsyncRead;
340     
341     if( pUnkOuter )
342         return CLASS_E_NOAGGREGATION;
343     
344     pAsyncRead = CoTaskMemAlloc(sizeof(AsyncReader));
345
346     if (!pAsyncRead)
347         return E_OUTOFMEMORY;
348
349     pAsyncRead->lpVtbl = &AsyncReader_Vtbl;
350     pAsyncRead->lpVtblFSF = &FileSource_Vtbl;
351     pAsyncRead->refCount = 1;
352     pAsyncRead->filterInfo.achName[0] = '\0';
353     pAsyncRead->filterInfo.pGraph = NULL;
354     pAsyncRead->pOutputPin = NULL;
355
356     InitializeCriticalSection(&pAsyncRead->csFilter);
357     pAsyncRead->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AsyncReader.csFilter");
358
359     pAsyncRead->pszFileName = NULL;
360     pAsyncRead->pmt = NULL;
361
362     *ppv = (LPVOID)pAsyncRead;
363
364     TRACE("-- created at %p\n", pAsyncRead);
365
366     return S_OK;
367 }
368
369 /** IUnknown methods **/
370
371 static HRESULT WINAPI AsyncReader_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
372 {
373     AsyncReader *This = (AsyncReader *)iface;
374
375     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
376
377     *ppv = NULL;
378
379     if (IsEqualIID(riid, &IID_IUnknown))
380         *ppv = (LPVOID)This;
381     else if (IsEqualIID(riid, &IID_IPersist))
382         *ppv = (LPVOID)This;
383     else if (IsEqualIID(riid, &IID_IMediaFilter))
384         *ppv = (LPVOID)This;
385     else if (IsEqualIID(riid, &IID_IBaseFilter))
386         *ppv = (LPVOID)This;
387     else if (IsEqualIID(riid, &IID_IFileSourceFilter))
388         *ppv = (LPVOID)(&This->lpVtblFSF);
389
390     if (*ppv)
391     {
392         IUnknown_AddRef((IUnknown *)(*ppv));
393         return S_OK;
394     }
395
396     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking))
397         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
398
399     return E_NOINTERFACE;
400 }
401
402 static ULONG WINAPI AsyncReader_AddRef(IBaseFilter * iface)
403 {
404     AsyncReader *This = (AsyncReader *)iface;
405     ULONG refCount = InterlockedIncrement(&This->refCount);
406     
407     TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
408     
409     return refCount;
410 }
411
412 static ULONG WINAPI AsyncReader_Release(IBaseFilter * iface)
413 {
414     AsyncReader *This = (AsyncReader *)iface;
415     ULONG refCount = InterlockedDecrement(&This->refCount);
416     
417     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
418     
419     if (!refCount)
420     {
421         if (This->pOutputPin)
422         {
423             IPin *pConnectedTo;
424             if(SUCCEEDED(IPin_ConnectedTo(This->pOutputPin, &pConnectedTo)))
425             {
426                 IPin_Disconnect(pConnectedTo);
427                 IPin_Release(pConnectedTo);
428             }
429             IPin_Disconnect(This->pOutputPin);
430             IPin_Release(This->pOutputPin);
431         }
432         This->csFilter.DebugInfo->Spare[0] = 0;
433         DeleteCriticalSection(&This->csFilter);
434         This->lpVtbl = NULL;
435         CoTaskMemFree(This);
436         return 0;
437     }
438     else
439         return refCount;
440 }
441
442 /** IPersist methods **/
443
444 static HRESULT WINAPI AsyncReader_GetClassID(IBaseFilter * iface, CLSID * pClsid)
445 {
446     TRACE("(%p)\n", pClsid);
447
448     *pClsid = CLSID_AsyncReader;
449
450     return S_OK;
451 }
452
453 /** IMediaFilter methods **/
454
455 static HRESULT WINAPI AsyncReader_Stop(IBaseFilter * iface)
456 {
457     AsyncReader *This = (AsyncReader *)iface;
458
459     TRACE("()\n");
460
461     This->state = State_Stopped;
462     
463     return S_OK;
464 }
465
466 static HRESULT WINAPI AsyncReader_Pause(IBaseFilter * iface)
467 {
468     AsyncReader *This = (AsyncReader *)iface;
469
470     TRACE("()\n");
471
472     This->state = State_Paused;
473
474     return S_OK;
475 }
476
477 static HRESULT WINAPI AsyncReader_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
478 {
479     AsyncReader *This = (AsyncReader *)iface;
480
481     TRACE("(%x%08x)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
482
483     This->state = State_Running;
484
485     return S_OK;
486 }
487
488 static HRESULT WINAPI AsyncReader_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
489 {
490     AsyncReader *This = (AsyncReader *)iface;
491
492     TRACE("(%u, %p)\n", dwMilliSecsTimeout, pState);
493
494     *pState = This->state;
495     
496     return S_OK;
497 }
498
499 static HRESULT WINAPI AsyncReader_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
500 {
501 /*    AsyncReader *This = (AsyncReader *)iface;*/
502
503     TRACE("(%p)\n", pClock);
504
505     return S_OK;
506 }
507
508 static HRESULT WINAPI AsyncReader_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
509 {
510 /*    AsyncReader *This = (AsyncReader *)iface;*/
511
512     TRACE("(%p)\n", ppClock);
513
514     return S_OK;
515 }
516
517 /** IBaseFilter methods **/
518
519 static HRESULT WINAPI AsyncReader_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
520 {
521     ENUMPINDETAILS epd;
522     AsyncReader *This = (AsyncReader *)iface;
523
524     TRACE("(%p)\n", ppEnum);
525
526     epd.cPins = This->pOutputPin ? 1 : 0;
527     epd.ppPins = &This->pOutputPin;
528     return IEnumPinsImpl_Construct(&epd, ppEnum);
529 }
530
531 static HRESULT WINAPI AsyncReader_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
532 {
533     FIXME("(%s, %p)\n", debugstr_w(Id), ppPin);
534
535     return E_NOTIMPL;
536 }
537
538 static HRESULT WINAPI AsyncReader_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
539 {
540     AsyncReader *This = (AsyncReader *)iface;
541
542     TRACE("(%p)\n", pInfo);
543
544     strcpyW(pInfo->achName, This->filterInfo.achName);
545     pInfo->pGraph = This->filterInfo.pGraph;
546
547     if (pInfo->pGraph)
548         IFilterGraph_AddRef(pInfo->pGraph);
549     
550     return S_OK;
551 }
552
553 static HRESULT WINAPI AsyncReader_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
554 {
555     AsyncReader *This = (AsyncReader *)iface;
556
557     TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
558
559     if (pName)
560         strcpyW(This->filterInfo.achName, pName);
561     else
562         *This->filterInfo.achName = 0;
563     This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
564
565     return S_OK;
566 }
567
568 static HRESULT WINAPI AsyncReader_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
569 {
570     FIXME("(%p)\n", pVendorInfo);
571
572     return E_NOTIMPL;
573 }
574
575 static const IBaseFilterVtbl AsyncReader_Vtbl =
576 {
577     AsyncReader_QueryInterface,
578     AsyncReader_AddRef,
579     AsyncReader_Release,
580     AsyncReader_GetClassID,
581     AsyncReader_Stop,
582     AsyncReader_Pause,
583     AsyncReader_Run,
584     AsyncReader_GetState,
585     AsyncReader_SetSyncSource,
586     AsyncReader_GetSyncSource,
587     AsyncReader_EnumPins,
588     AsyncReader_FindPin,
589     AsyncReader_QueryFilterInfo,
590     AsyncReader_JoinFilterGraph,
591     AsyncReader_QueryVendorInfo
592 };
593
594 static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv)
595 {
596     AsyncReader *This = impl_from_IFileSourceFilter(iface);
597
598     return IBaseFilter_QueryInterface((IFileSourceFilter*)&This->lpVtbl, riid, ppv);
599 }
600
601 static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface)
602 {
603     AsyncReader *This = impl_from_IFileSourceFilter(iface);
604
605     return IBaseFilter_AddRef((IFileSourceFilter*)&This->lpVtbl);
606 }
607
608 static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface)
609 {
610     AsyncReader *This = impl_from_IFileSourceFilter(iface);
611
612     return IBaseFilter_Release((IFileSourceFilter*)&This->lpVtbl);
613 }
614
615 static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
616 {
617     HRESULT hr;
618     HANDLE hFile;
619     IAsyncReader * pReader = NULL;
620     AsyncReader *This = impl_from_IFileSourceFilter(iface);
621
622     TRACE("(%s, %p)\n", debugstr_w(pszFileName), pmt);
623
624     /* open file */
625     /* FIXME: check the sharing values that native uses */
626     hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
627
628     if (hFile == INVALID_HANDLE_VALUE)
629     {
630         return HRESULT_FROM_WIN32(GetLastError());
631     }
632
633     /* create pin */
634     hr = FileAsyncReader_Construct(hFile, (IBaseFilter *)&This->lpVtbl, &This->csFilter, &This->pOutputPin);
635
636     if (SUCCEEDED(hr))
637         hr = IPin_QueryInterface(This->pOutputPin, &IID_IAsyncReader, (LPVOID *)&pReader);
638
639     /* store file name & media type */
640     if (SUCCEEDED(hr))
641     {
642         This->pszFileName = CoTaskMemAlloc((strlenW(pszFileName) + 1) * sizeof(WCHAR));
643         strcpyW(This->pszFileName, pszFileName);
644         This->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
645         if (!pmt)
646         {
647             This->pmt->bFixedSizeSamples = TRUE;
648             This->pmt->bTemporalCompression = FALSE;
649             This->pmt->cbFormat = 0;
650             This->pmt->pbFormat = NULL;
651             This->pmt->pUnk = NULL;
652             This->pmt->lSampleSize = 0;
653             This->pmt->formattype = FORMAT_None;
654             hr = GetClassMediaFile(pReader, pszFileName, &This->pmt->majortype, &This->pmt->subtype);
655             if (FAILED(hr))
656             {
657                 CoTaskMemFree(This->pmt);
658                 This->pmt = NULL;
659             }
660         }
661         else
662             CopyMediaType(This->pmt, pmt);
663     }
664
665     if (pReader)
666         IAsyncReader_Release(pReader);
667
668     if (FAILED(hr))
669     {
670         if (This->pOutputPin)
671         {
672             IPin_Release(This->pOutputPin);
673             This->pOutputPin = NULL;
674         }
675
676         CoTaskMemFree(This->pszFileName);
677         This->pszFileName = NULL;
678
679         CloseHandle(hFile);
680     }
681
682     /* FIXME: check return codes */
683     return hr;
684 }
685
686 static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter * iface, LPOLESTR * ppszFileName, AM_MEDIA_TYPE * pmt)
687 {
688     AsyncReader *This = impl_from_IFileSourceFilter(iface);
689     
690     TRACE("(%p, %p)\n", ppszFileName, pmt);
691
692     if (!ppszFileName)
693         return E_POINTER;
694
695     /* copy file name & media type if available, otherwise clear the outputs */
696     if (This->pszFileName)
697     {
698         *ppszFileName = CoTaskMemAlloc((strlenW(This->pszFileName) + 1) * sizeof(WCHAR));
699         strcpyW(*ppszFileName, This->pszFileName);
700     }
701     else
702         *ppszFileName = NULL;
703
704     if (pmt)
705     {
706         if (This->pmt)
707             CopyMediaType(pmt, This->pmt);
708         else
709             ZeroMemory(pmt, sizeof(*pmt));
710     }
711
712     return S_OK;
713 }
714
715 static const IFileSourceFilterVtbl FileSource_Vtbl = 
716 {
717     FileSource_QueryInterface,
718     FileSource_AddRef,
719     FileSource_Release,
720     FileSource_Load,
721     FileSource_GetCurFile
722 };
723
724
725 /* the dwUserData passed back to user */
726 typedef struct DATAREQUEST
727 {
728     IMediaSample * pSample; /* sample passed to us by user */
729     DWORD_PTR dwUserData; /* user data passed to us */
730     OVERLAPPED ovl; /* our overlapped structure */
731
732     struct DATAREQUEST * pNext; /* next data request in list */
733 } DATAREQUEST;
734
735 static void queue(DATAREQUEST * pHead, DATAREQUEST * pItem)
736 {
737     DATAREQUEST * pCurrent;
738     for (pCurrent = pHead; pCurrent->pNext; pCurrent = pCurrent->pNext)
739         ;
740     pCurrent->pNext = pItem;
741 }
742
743 typedef struct FileAsyncReader
744 {
745     OutputPin pin;
746     const struct IAsyncReaderVtbl * lpVtblAR;
747
748     HANDLE hFile;
749     HANDLE hEvent;
750     BOOL bFlushing;
751     DATAREQUEST * pHead; /* head of data request list */
752     CRITICAL_SECTION csList; /* critical section to protect operations on list */
753 } FileAsyncReader;
754
755 static inline FileAsyncReader *impl_from_IAsyncReader( IAsyncReader *iface )
756 {
757     return (FileAsyncReader *)((char*)iface - FIELD_OFFSET(FileAsyncReader, lpVtblAR));
758 }
759
760 static HRESULT AcceptProcAFR(LPVOID iface, const AM_MEDIA_TYPE *pmt)
761 {
762     AsyncReader *This = (AsyncReader *)iface;
763     
764     FIXME("(%p, %p)\n", iface, pmt);
765
766     if (IsEqualGUID(&pmt->majortype, &This->pmt->majortype) &&
767         IsEqualGUID(&pmt->subtype, &This->pmt->subtype) &&
768         IsEqualGUID(&pmt->formattype, &FORMAT_None))
769         return S_OK;
770     
771     return S_FALSE;
772 }
773
774 /* overriden pin functions */
775
776 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
777 {
778     FileAsyncReader *This = (FileAsyncReader *)iface;
779     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
780
781     *ppv = NULL;
782
783     if (IsEqualIID(riid, &IID_IUnknown))
784         *ppv = (LPVOID)This;
785     else if (IsEqualIID(riid, &IID_IPin))
786         *ppv = (LPVOID)This;
787     else if (IsEqualIID(riid, &IID_IAsyncReader))
788         *ppv = (LPVOID)&This->lpVtblAR;
789
790     if (*ppv)
791     {
792         IUnknown_AddRef((IUnknown *)(*ppv));
793         return S_OK;
794     }
795
796     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking))
797         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
798
799     return E_NOINTERFACE;
800 }
801
802 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface)
803 {
804     FileAsyncReader *This = (FileAsyncReader *)iface;
805     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
806     
807     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
808     
809     if (!refCount)
810     {
811         DATAREQUEST * pCurrent;
812         DATAREQUEST * pNext;
813         for (pCurrent = This->pHead; pCurrent; pCurrent = pNext)
814         {
815             pNext = pCurrent->pNext;
816             CoTaskMemFree(pCurrent);
817         }
818         CloseHandle(This->hFile);
819         CloseHandle(This->hEvent);
820         This->csList.DebugInfo->Spare[0] = 0;
821         DeleteCriticalSection(&This->csList);
822         CoTaskMemFree(This);
823         return 0;
824     }
825     return refCount;
826 }
827
828 static HRESULT WINAPI FileAsyncReaderPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
829 {
830     ENUMMEDIADETAILS emd;
831     FileAsyncReader *This = (FileAsyncReader *)iface;
832
833     TRACE("(%p)\n", ppEnum);
834
835     emd.cMediaTypes = 1;
836     emd.pMediaTypes = ((AsyncReader *)This->pin.pin.pinInfo.pFilter)->pmt;
837
838     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
839 }
840
841 static const IPinVtbl FileAsyncReaderPin_Vtbl = 
842 {
843     FileAsyncReaderPin_QueryInterface,
844     IPinImpl_AddRef,
845     FileAsyncReaderPin_Release,
846     OutputPin_Connect,
847     OutputPin_ReceiveConnection,
848     IPinImpl_Disconnect,
849     IPinImpl_ConnectedTo,
850     IPinImpl_ConnectionMediaType,
851     IPinImpl_QueryPinInfo,
852     IPinImpl_QueryDirection,
853     IPinImpl_QueryId,
854     IPinImpl_QueryAccept,
855     FileAsyncReaderPin_EnumMediaTypes,
856     IPinImpl_QueryInternalConnections,
857     OutputPin_EndOfStream,
858     OutputPin_BeginFlush,
859     OutputPin_EndFlush,
860     OutputPin_NewSegment
861 };
862
863 /* Function called as a helper to IPin_Connect */
864 /* specific AM_MEDIA_TYPE - it cannot be NULL */
865 /* this differs from standard OutputPin_ConnectSpecific only in that it
866  * doesn't need the IMemInputPin interface on the receiving pin */
867 static HRESULT FileAsyncReaderPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
868 {
869     OutputPin *This = (OutputPin *)iface;
870     HRESULT hr;
871
872     TRACE("(%p, %p)\n", pReceivePin, pmt);
873     dump_AM_MEDIA_TYPE(pmt);
874
875     /* FIXME: call queryacceptproc */
876
877     This->pin.pConnectedTo = pReceivePin;
878     IPin_AddRef(pReceivePin);
879     CopyMediaType(&This->pin.mtCurrent, pmt);
880
881     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
882
883     if (FAILED(hr))
884     {
885         IPin_Release(This->pin.pConnectedTo);
886         This->pin.pConnectedTo = NULL;
887         FreeMediaType(&This->pin.mtCurrent);
888     }
889
890     TRACE(" -- %x\n", hr);
891     return hr;
892 }
893
894 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
895 {
896     PIN_INFO piOutput;
897     HRESULT hr;
898
899     *ppPin = NULL;
900     piOutput.dir = PINDIR_OUTPUT;
901     piOutput.pFilter = pBaseFilter;
902     strcpyW(piOutput.achName, wszOutputPinName);
903     hr = OutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, NULL, pBaseFilter, AcceptProcAFR, pCritSec, ppPin);
904
905     if (SUCCEEDED(hr))
906     {
907         FileAsyncReader *pPinImpl =  (FileAsyncReader *)*ppPin;
908         pPinImpl->lpVtblAR = &FileAsyncReader_Vtbl;
909         pPinImpl->hFile = hFile;
910         pPinImpl->hEvent = CreateEventW(NULL, 0, 0, NULL);
911         pPinImpl->bFlushing = FALSE;
912         pPinImpl->pHead = NULL;
913         pPinImpl->pin.pConnectSpecific = FileAsyncReaderPin_ConnectSpecific;
914         InitializeCriticalSection(&pPinImpl->csList);
915         pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList");
916     }
917     return hr;
918 }
919
920 /* IAsyncReader */
921
922 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv)
923 {
924     FileAsyncReader *This = impl_from_IAsyncReader(iface);
925
926     return IPin_QueryInterface((IPin *)This, riid, ppv);
927 }
928
929 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
930 {
931     FileAsyncReader *This = impl_from_IAsyncReader(iface);
932
933     return IPin_AddRef((IPin *)This);
934 }
935
936 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
937 {
938     FileAsyncReader *This = impl_from_IAsyncReader(iface);
939
940     return IPin_Release((IPin *)This);
941 }
942
943 #define DEF_ALIGNMENT 1
944
945 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual)
946 {
947     HRESULT hr = S_OK;
948
949     TRACE("(%p, %p, %p)\n", pPreferred, pProps, ppActual);
950
951     if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0)
952         pProps->cbAlign = DEF_ALIGNMENT;
953
954     if (pPreferred)
955     {
956         ALLOCATOR_PROPERTIES PropsActual;
957         hr = IMemAllocator_SetProperties(pPreferred, pProps, &PropsActual);
958         /* FIXME: check we are still aligned */
959         if (SUCCEEDED(hr))
960         {
961             IMemAllocator_AddRef(pPreferred);
962             *ppActual = pPreferred;
963             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
964             return S_OK;
965         }
966     }
967
968     pPreferred = NULL;
969
970     hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred);
971
972     if (SUCCEEDED(hr))
973     {
974         ALLOCATOR_PROPERTIES PropsActual;
975         hr = IMemAllocator_SetProperties(pPreferred, pProps, &PropsActual);
976         /* FIXME: check we are still aligned */
977         if (SUCCEEDED(hr))
978         {
979             *ppActual = pPreferred;
980             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
981             return S_OK;
982         }
983     }
984
985     if (FAILED(hr))
986     {
987         *ppActual = NULL;
988         if (pPreferred)
989             IMemAllocator_Release(pPreferred);
990     }
991
992     TRACE("-- %x\n", hr);
993     return hr;
994 }
995
996 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples.
997  * however, this would be quite complicated to do and may be a bit error prone */
998 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser)
999 {
1000     REFERENCE_TIME Start;
1001     REFERENCE_TIME Stop;
1002     DATAREQUEST * pDataRq;
1003     BYTE * pBuffer;
1004     HRESULT hr = S_OK;
1005     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1006
1007     TRACE("(%p, %lx)\n", pSample, dwUser);
1008
1009     /* check flushing state */
1010     if (This->bFlushing)
1011         return VFW_E_WRONG_STATE;
1012
1013     if (!(pDataRq = CoTaskMemAlloc(sizeof(*pDataRq))))
1014         hr = E_OUTOFMEMORY;
1015
1016     /* get start and stop positions in bytes */
1017     if (SUCCEEDED(hr))
1018         hr = IMediaSample_GetTime(pSample, &Start, &Stop);
1019
1020     if (SUCCEEDED(hr))
1021         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1022
1023     if (SUCCEEDED(hr))
1024     {
1025         DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start);
1026
1027         pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start);
1028         pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8));
1029         pDataRq->ovl.hEvent = This->hEvent;
1030         pDataRq->dwUserData = dwUser;
1031         pDataRq->pNext = NULL;
1032         /* we violate traditional COM rules here by maintaining
1033          * a reference to the sample, but not calling AddRef, but
1034          * that's what MSDN says to do */
1035         pDataRq->pSample = pSample;
1036
1037         EnterCriticalSection(&This->csList);
1038         {
1039             if (This->pHead)
1040                 /* adds data request to end of list */
1041                 queue(This->pHead, pDataRq);
1042             else
1043                 This->pHead = pDataRq;
1044         }
1045         LeaveCriticalSection(&This->csList);
1046
1047         /* this is definitely not how it is implemented on Win9x
1048          * as they do not support async reads on files, but it is
1049          * sooo much easier to use this than messing around with threads!
1050          */
1051         if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl))
1052             hr = HRESULT_FROM_WIN32(GetLastError());
1053
1054         /* ERROR_IO_PENDING is not actually an error since this is what we want! */
1055         if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1056             hr = S_OK;
1057     }
1058
1059     if (FAILED(hr) && pDataRq)
1060     {
1061         EnterCriticalSection(&This->csList);
1062         {
1063             DATAREQUEST * pCurrent;
1064             for (pCurrent = This->pHead; pCurrent && pCurrent->pNext; pCurrent = pCurrent->pNext)
1065                 if (pCurrent->pNext == pDataRq)
1066                 {
1067                     pCurrent->pNext = pDataRq->pNext;
1068                     break;
1069                 }
1070         }
1071         LeaveCriticalSection(&This->csList);
1072         CoTaskMemFree(pDataRq);
1073     }
1074
1075     TRACE("-- %x\n", hr);
1076     return hr;
1077 }
1078
1079 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser)
1080 {
1081     HRESULT hr = S_OK;
1082     DWORD dwBytes = 0;
1083     DATAREQUEST * pDataRq = NULL;
1084     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1085
1086     TRACE("(%u, %p, %p)\n", dwTimeout, ppSample, pdwUser);
1087
1088     /* FIXME: we could do with improving this by waiting for an array of event handles
1089      * and then determining which one finished and removing that from the list, otherwise
1090      * we will end up waiting for longer than we should do, if a later request finishes
1091      * before an earlier one */
1092
1093     *ppSample = NULL;
1094     *pdwUser = 0;
1095
1096     /* we return immediately if flushing */
1097     if (This->bFlushing)
1098         hr = VFW_E_WRONG_STATE;
1099
1100     if (SUCCEEDED(hr))
1101     {
1102         /* wait for the read to finish or timeout */
1103         if (WaitForSingleObject(This->hEvent, dwTimeout) == WAIT_TIMEOUT)
1104             hr = VFW_E_TIMEOUT;
1105     }
1106     if (SUCCEEDED(hr))
1107     {
1108         EnterCriticalSection(&This->csList);
1109         {
1110             pDataRq = This->pHead;
1111             if (pDataRq == NULL)
1112                 hr = E_FAIL;
1113             else
1114                 This->pHead = pDataRq->pNext;
1115         }
1116         LeaveCriticalSection(&This->csList);
1117     }
1118
1119     if (SUCCEEDED(hr))
1120     {
1121         /* get any errors */
1122         if (!GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE))
1123             hr = HRESULT_FROM_WIN32(GetLastError());
1124     }
1125
1126     if (SUCCEEDED(hr))
1127     {
1128         IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes);
1129         *ppSample = pDataRq->pSample;
1130         *pdwUser = pDataRq->dwUserData;
1131     }
1132
1133     /* no need to close event handle since we will close it when the pin is destroyed */
1134     CoTaskMemFree(pDataRq);
1135     
1136     TRACE("-- %x\n", hr);
1137     return hr;
1138 }
1139
1140 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer);
1141
1142 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample)
1143 {
1144     BYTE * pBuffer;
1145     REFERENCE_TIME tStart;
1146     REFERENCE_TIME tStop;
1147     HRESULT hr;
1148
1149     TRACE("(%p)\n", pSample);
1150
1151     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
1152
1153     if (SUCCEEDED(hr))
1154         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1155
1156     if (SUCCEEDED(hr))
1157         hr = FileAsyncReader_SyncRead(iface, 
1158             BYTES_FROM_MEDIATIME(tStart),
1159             (LONG) BYTES_FROM_MEDIATIME(tStop - tStart),
1160             pBuffer);
1161
1162     TRACE("-- %x\n", hr);
1163     return hr;
1164 }
1165
1166 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer)
1167 {
1168     OVERLAPPED ovl;
1169     HRESULT hr = S_OK;
1170     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1171
1172     TRACE("(%x%08x, %d, %p)\n", (ULONG)(llPosition >> 32), (ULONG)llPosition, lLength, pBuffer);
1173
1174     ZeroMemory(&ovl, sizeof(ovl));
1175
1176     ovl.hEvent = CreateEventW(NULL, 0, 0, NULL);
1177     /* NOTE: llPosition is the actual byte position to start reading from */
1178     ovl.u.s.Offset = (DWORD) llPosition;
1179     ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8));
1180
1181     if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl))
1182         hr = HRESULT_FROM_WIN32(GetLastError());
1183
1184     if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1185         hr = S_OK;
1186
1187     if (SUCCEEDED(hr))
1188     {
1189         DWORD dwBytesRead;
1190
1191         if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE))
1192             hr = HRESULT_FROM_WIN32(GetLastError());
1193     }
1194
1195     CloseHandle(ovl.hEvent);
1196
1197     TRACE("-- %x\n", hr);
1198     return hr;
1199 }
1200
1201 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable)
1202 {
1203     DWORD dwSizeLow;
1204     DWORD dwSizeHigh;
1205     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1206
1207     TRACE("(%p, %p)\n", pTotal, pAvailable);
1208
1209     if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) &&
1210         (GetLastError() != NO_ERROR))
1211         return HRESULT_FROM_WIN32(GetLastError());
1212
1213     *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8);
1214
1215     *pAvailable = *pTotal;
1216
1217     return S_OK;
1218 }
1219
1220 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
1221 {
1222     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1223
1224     TRACE("()\n");
1225
1226     This->bFlushing = TRUE;
1227     CancelIo(This->hFile);
1228     SetEvent(This->hEvent);
1229     
1230     /* FIXME: free list */
1231
1232     return S_OK;
1233 }
1234
1235 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
1236 {
1237     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1238
1239     TRACE("()\n");
1240
1241     This->bFlushing = FALSE;
1242
1243     return S_OK;
1244 }
1245
1246 static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 
1247 {
1248     FileAsyncReader_QueryInterface,
1249     FileAsyncReader_AddRef,
1250     FileAsyncReader_Release,
1251     FileAsyncReader_RequestAllocator,
1252     FileAsyncReader_Request,
1253     FileAsyncReader_WaitForNext,
1254     FileAsyncReader_SyncReadAligned,
1255     FileAsyncReader_SyncRead,
1256     FileAsyncReader_Length,
1257     FileAsyncReader_BeginFlush,
1258     FileAsyncReader_EndFlush,
1259 };