ntdll: RtlSetControlSecurityDescriptor() implementation and test.
[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     DWORD lastpinchange;
50
51     IPin * pOutputPin;
52     LPOLESTR pszFileName;
53     AM_MEDIA_TYPE * pmt;
54 } AsyncReader;
55
56 static const IBaseFilterVtbl AsyncReader_Vtbl;
57 static const IFileSourceFilterVtbl FileSource_Vtbl;
58 static const IAsyncReaderVtbl FileAsyncReader_Vtbl;
59
60 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin);
61
62 static inline AsyncReader *impl_from_IFileSourceFilter( IFileSourceFilter *iface )
63 {
64     return (AsyncReader *)((char*)iface - FIELD_OFFSET(AsyncReader, lpVtblFSF));
65 }
66
67 static WCHAR const mediatype_name[11] = {
68     'M', 'e', 'd', 'i', 'a', ' ', 'T', 'y', 'p', 'e', 0 };
69 static WCHAR const subtype_name[8] = {
70     'S', 'u', 'b', 't', 'y', 'p', 'e', 0 };
71
72 static HRESULT process_extensions(HKEY hkeyExtensions, LPCOLESTR pszFileName, GUID * majorType, GUID * minorType)
73 {
74     WCHAR *extension;
75     LONG l;
76     HKEY hsub;
77     WCHAR keying[39];
78     DWORD size;
79
80     if (!pszFileName)
81         return E_POINTER;
82
83     /* Get the part of the name that matters */
84     extension = PathFindExtensionW(pszFileName);
85     if (*extension != '.')
86         return E_FAIL;
87
88     l = RegOpenKeyExW(hkeyExtensions, extension, 0, KEY_READ, &hsub);
89     if (l)
90         return E_FAIL;
91
92     size = sizeof(keying);
93     l = RegQueryValueExW(hsub, mediatype_name, NULL, NULL, (LPBYTE)keying, &size);
94     if (!l)
95         CLSIDFromString(keying, majorType);
96
97     size = sizeof(keying);
98     if (!l)
99         l = RegQueryValueExW(hsub, subtype_name, NULL, NULL, (LPBYTE)keying, &size);
100
101     if (!l)
102         CLSIDFromString(keying, minorType);
103
104     RegCloseKey(hsub);
105
106     if (!l)
107         return S_OK;
108     return E_FAIL;
109 }
110
111 static unsigned char byte_from_hex_char(WCHAR wHex)
112 {
113     switch (tolowerW(wHex))
114     {
115     case '0':
116     case '1':
117     case '2':
118     case '3':
119     case '4':
120     case '5':
121     case '6':
122     case '7':
123     case '8':
124     case '9':
125         return (wHex - '0') & 0xf;
126     case 'a':
127     case 'b':
128     case 'c':
129     case 'd':
130     case 'e':
131     case 'f':
132         return (wHex - 'a' + 10) & 0xf;
133     default:
134         return 0;
135     }
136 }
137
138 static HRESULT process_pattern_string(LPCWSTR wszPatternString, IAsyncReader * pReader)
139 {
140     ULONG ulOffset;
141     ULONG ulBytes;
142     BYTE * pbMask;
143     BYTE * pbValue;
144     BYTE * pbFile;
145     HRESULT hr = S_OK;
146     ULONG strpos;
147
148     TRACE("\t\tPattern string: %s\n", debugstr_w(wszPatternString));
149     
150     /* format: "offset, bytestocompare, mask, value" */
151
152     ulOffset = strtolW(wszPatternString, NULL, 10);
153
154     if (!(wszPatternString = strchrW(wszPatternString, ',')))
155         return E_INVALIDARG;
156
157     wszPatternString++; /* skip ',' */
158
159     ulBytes = strtolW(wszPatternString, NULL, 10);
160
161     pbMask = HeapAlloc(GetProcessHeap(), 0, ulBytes);
162     pbValue = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulBytes);
163     pbFile = HeapAlloc(GetProcessHeap(), 0, ulBytes);
164
165     /* default mask is match everything */
166     memset(pbMask, 0xFF, ulBytes);
167
168     if (!(wszPatternString = strchrW(wszPatternString, ',')))
169         hr = E_INVALIDARG;
170
171     if (hr == S_OK)
172     {
173         wszPatternString++; /* skip ',' */
174         while (!isxdigitW(*wszPatternString) && (*wszPatternString != ',')) wszPatternString++;
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     pAsyncRead->lastpinchange = GetTickCount();
356     pAsyncRead->state = State_Stopped;
357
358     InitializeCriticalSection(&pAsyncRead->csFilter);
359     pAsyncRead->csFilter.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": AsyncReader.csFilter");
360
361     pAsyncRead->pszFileName = NULL;
362     pAsyncRead->pmt = NULL;
363
364     *ppv = (LPVOID)pAsyncRead;
365
366     TRACE("-- created at %p\n", pAsyncRead);
367
368     return S_OK;
369 }
370
371 /** IUnknown methods **/
372
373 static HRESULT WINAPI AsyncReader_QueryInterface(IBaseFilter * iface, REFIID riid, LPVOID * ppv)
374 {
375     AsyncReader *This = (AsyncReader *)iface;
376
377     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
378
379     *ppv = NULL;
380
381     if (IsEqualIID(riid, &IID_IUnknown))
382         *ppv = (LPVOID)This;
383     else if (IsEqualIID(riid, &IID_IPersist))
384         *ppv = (LPVOID)This;
385     else if (IsEqualIID(riid, &IID_IMediaFilter))
386         *ppv = (LPVOID)This;
387     else if (IsEqualIID(riid, &IID_IBaseFilter))
388         *ppv = (LPVOID)This;
389     else if (IsEqualIID(riid, &IID_IFileSourceFilter))
390         *ppv = (LPVOID)(&This->lpVtblFSF);
391
392     if (*ppv)
393     {
394         IUnknown_AddRef((IUnknown *)(*ppv));
395         return S_OK;
396     }
397
398     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking) && !IsEqualIID(riid, &IID_IVideoWindow))
399         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
400
401     return E_NOINTERFACE;
402 }
403
404 static ULONG WINAPI AsyncReader_AddRef(IBaseFilter * iface)
405 {
406     AsyncReader *This = (AsyncReader *)iface;
407     ULONG refCount = InterlockedIncrement(&This->refCount);
408     
409     TRACE("(%p)->() AddRef from %d\n", This, refCount - 1);
410     
411     return refCount;
412 }
413
414 static ULONG WINAPI AsyncReader_Release(IBaseFilter * iface)
415 {
416     AsyncReader *This = (AsyncReader *)iface;
417     ULONG refCount = InterlockedDecrement(&This->refCount);
418     
419     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
420     
421     if (!refCount)
422     {
423         if (This->pOutputPin)
424         {
425             IPin *pConnectedTo;
426             if(SUCCEEDED(IPin_ConnectedTo(This->pOutputPin, &pConnectedTo)))
427             {
428                 IPin_Disconnect(pConnectedTo);
429                 IPin_Release(pConnectedTo);
430             }
431             IPin_Disconnect(This->pOutputPin);
432             IPin_Release(This->pOutputPin);
433         }
434         This->csFilter.DebugInfo->Spare[0] = 0;
435         DeleteCriticalSection(&This->csFilter);
436         This->lpVtbl = NULL;
437         CoTaskMemFree(This->pszFileName);
438         if (This->pmt)
439             FreeMediaType(This->pmt);
440         CoTaskMemFree(This);
441         return 0;
442     }
443     else
444         return refCount;
445 }
446
447 /** IPersist methods **/
448
449 static HRESULT WINAPI AsyncReader_GetClassID(IBaseFilter * iface, CLSID * pClsid)
450 {
451     TRACE("(%p)\n", pClsid);
452
453     *pClsid = CLSID_AsyncReader;
454
455     return S_OK;
456 }
457
458 /** IMediaFilter methods **/
459
460 static HRESULT WINAPI AsyncReader_Stop(IBaseFilter * iface)
461 {
462     AsyncReader *This = (AsyncReader *)iface;
463
464     TRACE("()\n");
465
466     This->state = State_Stopped;
467     
468     return S_OK;
469 }
470
471 static HRESULT WINAPI AsyncReader_Pause(IBaseFilter * iface)
472 {
473     AsyncReader *This = (AsyncReader *)iface;
474
475     TRACE("()\n");
476
477     This->state = State_Paused;
478
479     return S_OK;
480 }
481
482 static HRESULT WINAPI AsyncReader_Run(IBaseFilter * iface, REFERENCE_TIME tStart)
483 {
484     AsyncReader *This = (AsyncReader *)iface;
485
486     TRACE("(%x%08x)\n", (ULONG)(tStart >> 32), (ULONG)tStart);
487
488     This->state = State_Running;
489
490     return S_OK;
491 }
492
493 static HRESULT WINAPI AsyncReader_GetState(IBaseFilter * iface, DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
494 {
495     AsyncReader *This = (AsyncReader *)iface;
496
497     TRACE("(%u, %p)\n", dwMilliSecsTimeout, pState);
498
499     *pState = This->state;
500     
501     return S_OK;
502 }
503
504 static HRESULT WINAPI AsyncReader_SetSyncSource(IBaseFilter * iface, IReferenceClock *pClock)
505 {
506 /*    AsyncReader *This = (AsyncReader *)iface;*/
507
508     TRACE("(%p)\n", pClock);
509
510     return S_OK;
511 }
512
513 static HRESULT WINAPI AsyncReader_GetSyncSource(IBaseFilter * iface, IReferenceClock **ppClock)
514 {
515 /*    AsyncReader *This = (AsyncReader *)iface;*/
516
517     TRACE("(%p)\n", ppClock);
518
519     return S_OK;
520 }
521
522 /** IBaseFilter methods **/
523
524 static HRESULT AsyncReader_GetPin(IBaseFilter *iface, ULONG pos, IPin **pin, DWORD *lastsynctick)
525 {
526     AsyncReader *This = (AsyncReader *)iface;
527
528     /* Our pins are almost static, not changing so setting static tick count is ok */
529     *lastsynctick = This->lastpinchange;
530
531     if (pos >= 1 || !This->pOutputPin)
532         return S_FALSE;
533
534     *pin = (IPin *)This->pOutputPin;
535     IPin_AddRef(*pin);
536     return S_OK;
537 }
538
539 static HRESULT WINAPI AsyncReader_EnumPins(IBaseFilter * iface, IEnumPins **ppEnum)
540 {
541     AsyncReader *This = (AsyncReader *)iface;
542
543     TRACE("(%p/%p)->(%p)\n", This, iface, ppEnum);
544
545     return IEnumPinsImpl_Construct(ppEnum, AsyncReader_GetPin, iface);
546 }
547
548 static HRESULT WINAPI AsyncReader_FindPin(IBaseFilter * iface, LPCWSTR Id, IPin **ppPin)
549 {
550     FIXME("(%s, %p)\n", debugstr_w(Id), ppPin);
551
552     return E_NOTIMPL;
553 }
554
555 static HRESULT WINAPI AsyncReader_QueryFilterInfo(IBaseFilter * iface, FILTER_INFO *pInfo)
556 {
557     AsyncReader *This = (AsyncReader *)iface;
558
559     TRACE("(%p)\n", pInfo);
560
561     strcpyW(pInfo->achName, This->filterInfo.achName);
562     pInfo->pGraph = This->filterInfo.pGraph;
563
564     if (pInfo->pGraph)
565         IFilterGraph_AddRef(pInfo->pGraph);
566     
567     return S_OK;
568 }
569
570 static HRESULT WINAPI AsyncReader_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName)
571 {
572     AsyncReader *This = (AsyncReader *)iface;
573
574     TRACE("(%p, %s)\n", pGraph, debugstr_w(pName));
575
576     if (pName)
577         strcpyW(This->filterInfo.achName, pName);
578     else
579         *This->filterInfo.achName = 0;
580     This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */
581
582     return S_OK;
583 }
584
585 static HRESULT WINAPI AsyncReader_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo)
586 {
587     FIXME("(%p)\n", pVendorInfo);
588
589     return E_NOTIMPL;
590 }
591
592 static const IBaseFilterVtbl AsyncReader_Vtbl =
593 {
594     AsyncReader_QueryInterface,
595     AsyncReader_AddRef,
596     AsyncReader_Release,
597     AsyncReader_GetClassID,
598     AsyncReader_Stop,
599     AsyncReader_Pause,
600     AsyncReader_Run,
601     AsyncReader_GetState,
602     AsyncReader_SetSyncSource,
603     AsyncReader_GetSyncSource,
604     AsyncReader_EnumPins,
605     AsyncReader_FindPin,
606     AsyncReader_QueryFilterInfo,
607     AsyncReader_JoinFilterGraph,
608     AsyncReader_QueryVendorInfo
609 };
610
611 static HRESULT WINAPI FileSource_QueryInterface(IFileSourceFilter * iface, REFIID riid, LPVOID * ppv)
612 {
613     AsyncReader *This = impl_from_IFileSourceFilter(iface);
614
615     return IBaseFilter_QueryInterface((IFileSourceFilter*)&This->lpVtbl, riid, ppv);
616 }
617
618 static ULONG WINAPI FileSource_AddRef(IFileSourceFilter * iface)
619 {
620     AsyncReader *This = impl_from_IFileSourceFilter(iface);
621
622     return IBaseFilter_AddRef((IFileSourceFilter*)&This->lpVtbl);
623 }
624
625 static ULONG WINAPI FileSource_Release(IFileSourceFilter * iface)
626 {
627     AsyncReader *This = impl_from_IFileSourceFilter(iface);
628
629     return IBaseFilter_Release((IFileSourceFilter*)&This->lpVtbl);
630 }
631
632 static HRESULT WINAPI FileSource_Load(IFileSourceFilter * iface, LPCOLESTR pszFileName, const AM_MEDIA_TYPE * pmt)
633 {
634     HRESULT hr;
635     HANDLE hFile;
636     IAsyncReader * pReader = NULL;
637     AsyncReader *This = impl_from_IFileSourceFilter(iface);
638
639     TRACE("(%s, %p)\n", debugstr_w(pszFileName), pmt);
640
641     /* open file */
642     /* FIXME: check the sharing values that native uses */
643     hFile = CreateFileW(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
644
645     if (hFile == INVALID_HANDLE_VALUE)
646     {
647         return HRESULT_FROM_WIN32(GetLastError());
648     }
649
650     /* create pin */
651     hr = FileAsyncReader_Construct(hFile, (IBaseFilter *)&This->lpVtbl, &This->csFilter, &This->pOutputPin);
652     This->lastpinchange = GetTickCount();
653
654     if (SUCCEEDED(hr))
655         hr = IPin_QueryInterface(This->pOutputPin, &IID_IAsyncReader, (LPVOID *)&pReader);
656
657     /* store file name & media type */
658     if (SUCCEEDED(hr))
659     {
660         CoTaskMemFree(This->pszFileName);
661         if (This->pmt)
662             FreeMediaType(This->pmt);
663
664         This->pszFileName = CoTaskMemAlloc((strlenW(pszFileName) + 1) * sizeof(WCHAR));
665         strcpyW(This->pszFileName, pszFileName);
666
667         This->pmt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
668         if (!pmt)
669         {
670             This->pmt->bFixedSizeSamples = TRUE;
671             This->pmt->bTemporalCompression = FALSE;
672             This->pmt->cbFormat = 0;
673             This->pmt->pbFormat = NULL;
674             This->pmt->pUnk = NULL;
675             This->pmt->lSampleSize = 0;
676             This->pmt->formattype = FORMAT_None;
677             hr = GetClassMediaFile(pReader, pszFileName, &This->pmt->majortype, &This->pmt->subtype);
678             if (FAILED(hr))
679             {
680                 CoTaskMemFree(This->pmt);
681                 This->pmt = NULL;
682             }
683         }
684         else
685             CopyMediaType(This->pmt, pmt);
686     }
687
688     if (pReader)
689         IAsyncReader_Release(pReader);
690
691     if (FAILED(hr))
692     {
693         if (This->pOutputPin)
694         {
695             IPin_Release(This->pOutputPin);
696             This->pOutputPin = NULL;
697         }
698
699         CoTaskMemFree(This->pszFileName);
700         if (This->pmt)
701             FreeMediaType(This->pmt);
702         This->pszFileName = NULL;
703         This->pmt = NULL;
704
705         CloseHandle(hFile);
706     }
707
708     /* FIXME: check return codes */
709     return hr;
710 }
711
712 static HRESULT WINAPI FileSource_GetCurFile(IFileSourceFilter * iface, LPOLESTR * ppszFileName, AM_MEDIA_TYPE * pmt)
713 {
714     AsyncReader *This = impl_from_IFileSourceFilter(iface);
715     
716     TRACE("(%p, %p)\n", ppszFileName, pmt);
717
718     if (!ppszFileName)
719         return E_POINTER;
720
721     /* copy file name & media type if available, otherwise clear the outputs */
722     if (This->pszFileName)
723     {
724         *ppszFileName = CoTaskMemAlloc((strlenW(This->pszFileName) + 1) * sizeof(WCHAR));
725         strcpyW(*ppszFileName, This->pszFileName);
726     }
727     else
728         *ppszFileName = NULL;
729
730     if (pmt)
731     {
732         if (This->pmt)
733             CopyMediaType(pmt, This->pmt);
734         else
735             ZeroMemory(pmt, sizeof(*pmt));
736     }
737
738     return S_OK;
739 }
740
741 static const IFileSourceFilterVtbl FileSource_Vtbl = 
742 {
743     FileSource_QueryInterface,
744     FileSource_AddRef,
745     FileSource_Release,
746     FileSource_Load,
747     FileSource_GetCurFile
748 };
749
750
751 /* the dwUserData passed back to user */
752 typedef struct DATAREQUEST
753 {
754     IMediaSample * pSample; /* sample passed to us by user */
755     DWORD_PTR dwUserData; /* user data passed to us */
756     OVERLAPPED ovl; /* our overlapped structure */
757 } DATAREQUEST;
758
759 typedef struct FileAsyncReader
760 {
761     OutputPin pin;
762     const struct IAsyncReaderVtbl * lpVtblAR;
763
764     HANDLE hFile;
765     BOOL bFlushing;
766     /* Why would you need more? Every sample has its own handle */
767     LONG queued_number;
768     LONG samples;
769     LONG oldest_sample;
770     CRITICAL_SECTION csList; /* critical section to prevent concurrency issues */
771     DATAREQUEST *sample_list;
772
773     /* Have a handle for every sample, and then one more as flushing handle */
774     HANDLE *handle_list;
775 } FileAsyncReader;
776
777 static inline FileAsyncReader *impl_from_IAsyncReader( IAsyncReader *iface )
778 {
779     return (FileAsyncReader *)((char*)iface - FIELD_OFFSET(FileAsyncReader, lpVtblAR));
780 }
781
782 static HRESULT AcceptProcAFR(LPVOID iface, const AM_MEDIA_TYPE *pmt)
783 {
784     AsyncReader *This = (AsyncReader *)iface;
785     
786     FIXME("(%p, %p)\n", iface, pmt);
787
788     if (IsEqualGUID(&pmt->majortype, &This->pmt->majortype) &&
789         IsEqualGUID(&pmt->subtype, &This->pmt->subtype) &&
790         IsEqualGUID(&pmt->formattype, &FORMAT_None))
791         return S_OK;
792     
793     return S_FALSE;
794 }
795
796 /* overridden pin functions */
797
798 static HRESULT WINAPI FileAsyncReaderPin_QueryInterface(IPin * iface, REFIID riid, LPVOID * ppv)
799 {
800     FileAsyncReader *This = (FileAsyncReader *)iface;
801     TRACE("(%s, %p)\n", qzdebugstr_guid(riid), ppv);
802
803     *ppv = NULL;
804
805     if (IsEqualIID(riid, &IID_IUnknown))
806         *ppv = (LPVOID)This;
807     else if (IsEqualIID(riid, &IID_IPin))
808         *ppv = (LPVOID)This;
809     else if (IsEqualIID(riid, &IID_IAsyncReader))
810         *ppv = (LPVOID)&This->lpVtblAR;
811
812     if (*ppv)
813     {
814         IUnknown_AddRef((IUnknown *)(*ppv));
815         return S_OK;
816     }
817
818     if (!IsEqualIID(riid, &IID_IPin) && !IsEqualIID(riid, &IID_IMediaSeeking))
819         FIXME("No interface for %s!\n", qzdebugstr_guid(riid));
820
821     return E_NOINTERFACE;
822 }
823
824 static ULONG WINAPI FileAsyncReaderPin_Release(IPin * iface)
825 {
826     FileAsyncReader *This = (FileAsyncReader *)iface;
827     ULONG refCount = InterlockedDecrement(&This->pin.pin.refCount);
828     int x;
829
830     TRACE("(%p)->() Release from %d\n", This, refCount + 1);
831
832     if (!refCount)
833     {
834         CoTaskMemFree(This->sample_list);
835         if (This->handle_list)
836         {
837             for (x = 0; x <= This->samples; ++x)
838                 CloseHandle(This->handle_list[x]);
839             CoTaskMemFree(This->handle_list);
840         }
841         CloseHandle(This->hFile);
842         This->csList.DebugInfo->Spare[0] = 0;
843         DeleteCriticalSection(&This->csList);
844         CoTaskMemFree(This);
845         return 0;
846     }
847     return refCount;
848 }
849
850 static HRESULT WINAPI FileAsyncReaderPin_EnumMediaTypes(IPin * iface, IEnumMediaTypes ** ppEnum)
851 {
852     ENUMMEDIADETAILS emd;
853     FileAsyncReader *This = (FileAsyncReader *)iface;
854
855     TRACE("(%p)\n", ppEnum);
856
857     emd.cMediaTypes = 1;
858     emd.pMediaTypes = ((AsyncReader *)This->pin.pin.pinInfo.pFilter)->pmt;
859
860     return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
861 }
862
863 static const IPinVtbl FileAsyncReaderPin_Vtbl = 
864 {
865     FileAsyncReaderPin_QueryInterface,
866     IPinImpl_AddRef,
867     FileAsyncReaderPin_Release,
868     OutputPin_Connect,
869     OutputPin_ReceiveConnection,
870     IPinImpl_Disconnect,
871     IPinImpl_ConnectedTo,
872     IPinImpl_ConnectionMediaType,
873     IPinImpl_QueryPinInfo,
874     IPinImpl_QueryDirection,
875     IPinImpl_QueryId,
876     IPinImpl_QueryAccept,
877     FileAsyncReaderPin_EnumMediaTypes,
878     IPinImpl_QueryInternalConnections,
879     OutputPin_EndOfStream,
880     OutputPin_BeginFlush,
881     OutputPin_EndFlush,
882     OutputPin_NewSegment
883 };
884
885 /* Function called as a helper to IPin_Connect */
886 /* specific AM_MEDIA_TYPE - it cannot be NULL */
887 /* this differs from standard OutputPin_ConnectSpecific only in that it
888  * doesn't need the IMemInputPin interface on the receiving pin */
889 static HRESULT FileAsyncReaderPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
890 {
891     OutputPin *This = (OutputPin *)iface;
892     HRESULT hr;
893
894     TRACE("(%p, %p)\n", pReceivePin, pmt);
895     dump_AM_MEDIA_TYPE(pmt);
896
897     /* FIXME: call queryacceptproc */
898
899     This->pin.pConnectedTo = pReceivePin;
900     IPin_AddRef(pReceivePin);
901     CopyMediaType(&This->pin.mtCurrent, pmt);
902
903     hr = IPin_ReceiveConnection(pReceivePin, iface, pmt);
904
905     if (FAILED(hr))
906     {
907         IPin_Release(This->pin.pConnectedTo);
908         This->pin.pConnectedTo = NULL;
909         FreeMediaType(&This->pin.mtCurrent);
910     }
911
912     TRACE(" -- %x\n", hr);
913     return hr;
914 }
915
916 static HRESULT FileAsyncReader_Construct(HANDLE hFile, IBaseFilter * pBaseFilter, LPCRITICAL_SECTION pCritSec, IPin ** ppPin)
917 {
918     PIN_INFO piOutput;
919     HRESULT hr;
920
921     *ppPin = NULL;
922     piOutput.dir = PINDIR_OUTPUT;
923     piOutput.pFilter = pBaseFilter;
924     strcpyW(piOutput.achName, wszOutputPinName);
925     hr = OutputPin_Construct(&FileAsyncReaderPin_Vtbl, sizeof(FileAsyncReader), &piOutput, NULL, pBaseFilter, AcceptProcAFR, pCritSec, ppPin);
926
927     if (SUCCEEDED(hr))
928     {
929         FileAsyncReader *pPinImpl =  (FileAsyncReader *)*ppPin;
930         pPinImpl->lpVtblAR = &FileAsyncReader_Vtbl;
931         pPinImpl->hFile = hFile;
932         pPinImpl->bFlushing = FALSE;
933         pPinImpl->sample_list = NULL;
934         pPinImpl->handle_list = NULL;
935         pPinImpl->queued_number = 0;
936         pPinImpl->pin.pConnectSpecific = FileAsyncReaderPin_ConnectSpecific;
937         InitializeCriticalSection(&pPinImpl->csList);
938         pPinImpl->csList.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": FileAsyncReader.csList");
939     }
940     return hr;
941 }
942
943 /* IAsyncReader */
944
945 static HRESULT WINAPI FileAsyncReader_QueryInterface(IAsyncReader * iface, REFIID riid, LPVOID * ppv)
946 {
947     FileAsyncReader *This = impl_from_IAsyncReader(iface);
948
949     return IPin_QueryInterface((IPin *)This, riid, ppv);
950 }
951
952 static ULONG WINAPI FileAsyncReader_AddRef(IAsyncReader * iface)
953 {
954     FileAsyncReader *This = impl_from_IAsyncReader(iface);
955
956     return IPin_AddRef((IPin *)This);
957 }
958
959 static ULONG WINAPI FileAsyncReader_Release(IAsyncReader * iface)
960 {
961     FileAsyncReader *This = impl_from_IAsyncReader(iface);
962
963     return IPin_Release((IPin *)This);
964 }
965
966 #define DEF_ALIGNMENT 1
967
968 static HRESULT WINAPI FileAsyncReader_RequestAllocator(IAsyncReader * iface, IMemAllocator * pPreferred, ALLOCATOR_PROPERTIES * pProps, IMemAllocator ** ppActual)
969 {
970     FileAsyncReader *This = impl_from_IAsyncReader(iface);
971
972     HRESULT hr = S_OK;
973
974     TRACE("(%p, %p, %p)\n", pPreferred, pProps, ppActual);
975
976     if (!pProps->cbAlign || (pProps->cbAlign % DEF_ALIGNMENT) != 0)
977         pProps->cbAlign = DEF_ALIGNMENT;
978
979     if (pPreferred)
980     {
981         hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
982         /* FIXME: check we are still aligned */
983         if (SUCCEEDED(hr))
984         {
985             IMemAllocator_AddRef(pPreferred);
986             *ppActual = pPreferred;
987             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
988             goto done;
989         }
990     }
991
992     pPreferred = NULL;
993
994     hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC, &IID_IMemAllocator, (LPVOID *)&pPreferred);
995
996     if (SUCCEEDED(hr))
997     {
998         hr = IMemAllocator_SetProperties(pPreferred, pProps, pProps);
999         /* FIXME: check we are still aligned */
1000         if (SUCCEEDED(hr))
1001         {
1002             *ppActual = pPreferred;
1003             TRACE("FileAsyncReader_RequestAllocator -- %x\n", hr);
1004         }
1005     }
1006
1007 done:
1008     if (SUCCEEDED(hr))
1009     {
1010         CoTaskMemFree(This->sample_list);
1011         if (This->handle_list)
1012         {
1013             int x;
1014             for (x = 0; x <= This->samples; ++x)
1015                 CloseHandle(This->handle_list[x]);
1016             CoTaskMemFree(This->handle_list);
1017         }
1018
1019         This->samples = pProps->cBuffers;
1020         This->oldest_sample = 0;
1021         TRACE("Samples: %u\n", This->samples);
1022         This->sample_list = CoTaskMemAlloc(sizeof(This->sample_list[0]) * pProps->cBuffers);
1023         This->handle_list = CoTaskMemAlloc(sizeof(HANDLE) * pProps->cBuffers * 2);
1024
1025         if (This->sample_list && This->handle_list)
1026         {
1027             int x;
1028             ZeroMemory(This->sample_list, sizeof(This->sample_list[0]) * pProps->cBuffers);
1029             for (x = 0; x < This->samples; ++x)
1030             {
1031                 This->sample_list[x].ovl.hEvent = This->handle_list[x] = CreateEventW(NULL, 0, 0, NULL);
1032                 if (x + 1 < This->samples)
1033                     This->handle_list[This->samples + 1 + x] = This->handle_list[x];
1034             }
1035             This->handle_list[This->samples] = CreateEventW(NULL, 1, 0, NULL);
1036             This->pin.allocProps = *pProps;
1037         }
1038         else
1039         {
1040             hr = E_OUTOFMEMORY;
1041             CoTaskMemFree(This->sample_list);
1042             CoTaskMemFree(This->handle_list);
1043             This->samples = 0;
1044             This->sample_list = NULL;
1045             This->handle_list = NULL;
1046         }
1047     }
1048
1049     if (FAILED(hr))
1050     {
1051         *ppActual = NULL;
1052         if (pPreferred)
1053             IMemAllocator_Release(pPreferred);
1054     }
1055
1056     TRACE("-- %x\n", hr);
1057     return hr;
1058 }
1059
1060 /* we could improve the Request/WaitForNext mechanism by allowing out of order samples.
1061  * however, this would be quite complicated to do and may be a bit error prone */
1062 static HRESULT WINAPI FileAsyncReader_Request(IAsyncReader * iface, IMediaSample * pSample, DWORD_PTR dwUser)
1063 {
1064     HRESULT hr = S_OK;
1065     REFERENCE_TIME Start;
1066     REFERENCE_TIME Stop;
1067     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1068     LPBYTE pBuffer = NULL;
1069
1070     TRACE("(%p, %lx)\n", pSample, dwUser);
1071
1072     if (!pSample)
1073         return E_POINTER;
1074
1075     /* get start and stop positions in bytes */
1076     if (SUCCEEDED(hr))
1077         hr = IMediaSample_GetTime(pSample, &Start, &Stop);
1078
1079     if (SUCCEEDED(hr))
1080         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1081
1082     EnterCriticalSection(&This->csList);
1083     if (This->bFlushing)
1084     {
1085         LeaveCriticalSection(&This->csList);
1086         return VFW_E_WRONG_STATE;
1087     }
1088
1089     if (SUCCEEDED(hr))
1090     {
1091         DWORD dwLength = (DWORD) BYTES_FROM_MEDIATIME(Stop - Start);
1092         DATAREQUEST *pDataRq;
1093         int x;
1094
1095         /* Try to insert above the waiting sample if possible */
1096         for (x = This->oldest_sample; x < This->samples; ++x)
1097         {
1098             if (!This->sample_list[x].pSample)
1099                 break;
1100         }
1101
1102         if (x >= This->samples)
1103             for (x = 0; x < This->oldest_sample; ++x)
1104             {
1105                 if (!This->sample_list[x].pSample)
1106                     break;
1107             }
1108
1109         /* There must be a sample we have found */
1110         assert(x < This->samples);
1111         ++This->queued_number;
1112
1113         pDataRq = This->sample_list + x;
1114
1115         pDataRq->ovl.u.s.Offset = (DWORD) BYTES_FROM_MEDIATIME(Start);
1116         pDataRq->ovl.u.s.OffsetHigh = (DWORD)(BYTES_FROM_MEDIATIME(Start) >> (sizeof(DWORD) * 8));
1117         pDataRq->dwUserData = dwUser;
1118
1119         /* we violate traditional COM rules here by maintaining
1120          * a reference to the sample, but not calling AddRef, but
1121          * that's what MSDN says to do */
1122         pDataRq->pSample = pSample;
1123
1124         /* this is definitely not how it is implemented on Win9x
1125          * as they do not support async reads on files, but it is
1126          * sooo much easier to use this than messing around with threads!
1127          */
1128         if (!ReadFile(This->hFile, pBuffer, dwLength, NULL, &pDataRq->ovl))
1129             hr = HRESULT_FROM_WIN32(GetLastError());
1130
1131         /* ERROR_IO_PENDING is not actually an error since this is what we want! */
1132         if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1133             hr = S_OK;
1134     }
1135
1136     LeaveCriticalSection(&This->csList);
1137
1138     TRACE("-- %x\n", hr);
1139     return hr;
1140 }
1141
1142 static HRESULT WINAPI FileAsyncReader_WaitForNext(IAsyncReader * iface, DWORD dwTimeout, IMediaSample ** ppSample, DWORD_PTR * pdwUser)
1143 {
1144     HRESULT hr = S_OK;
1145     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1146     DWORD buffer = ~0;
1147
1148     TRACE("(%u, %p, %p)\n", dwTimeout, ppSample, pdwUser);
1149
1150     *ppSample = NULL;
1151     *pdwUser = 0;
1152
1153     EnterCriticalSection(&This->csList);
1154     if (!This->bFlushing)
1155     {
1156         LONG oldest = This->oldest_sample;
1157
1158         if (!This->queued_number)
1159         {
1160             /* It could be that nothing is queued right now, but that can be fixed */
1161             WARN("Called without samples in queue and not flushing!!\n");
1162         }
1163         LeaveCriticalSection(&This->csList);
1164
1165         /* wait for an object to read, or time out */
1166         buffer = WaitForMultipleObjectsEx(This->samples+1, This->handle_list + oldest, FALSE, dwTimeout, TRUE);
1167
1168         EnterCriticalSection(&This->csList);
1169         if (buffer <= This->samples)
1170         {
1171             /* Re-scale the buffer back to normal */
1172             buffer += oldest;
1173
1174             /* Uh oh, we overshot the flusher handle, renormalize it back to 0..Samples-1 */
1175             if (buffer > This->samples)
1176                 buffer -= This->samples + 1;
1177             assert(buffer <= This->samples);
1178         }
1179
1180         if (buffer >= This->samples)
1181         {
1182             if (buffer != This->samples)
1183             {
1184                 FIXME("Returned: %u (%08x)\n", buffer, GetLastError());
1185                 hr = VFW_E_TIMEOUT;
1186             }
1187             else
1188                 hr = VFW_E_WRONG_STATE;
1189             buffer = ~0;
1190         }
1191         else
1192             --This->queued_number;
1193     }
1194
1195     if (This->bFlushing && buffer == ~0)
1196     {
1197         for (buffer = 0; buffer < This->samples; ++buffer)
1198         {
1199             if (This->sample_list[buffer].pSample)
1200             {
1201                 ResetEvent(This->handle_list[buffer]);
1202                 break;
1203             }
1204         }
1205         if (buffer == This->samples)
1206         {
1207             assert(!This->queued_number);
1208             hr = VFW_E_TIMEOUT;
1209         }
1210         else
1211         {
1212             --This->queued_number;
1213             hr = S_OK;
1214         }
1215     }
1216
1217     if (SUCCEEDED(hr))
1218     {
1219         REFERENCE_TIME rtStart, rtStop;
1220         REFERENCE_TIME rtSampleStart, rtSampleStop;
1221         DATAREQUEST *pDataRq = This->sample_list + buffer;
1222         DWORD dwBytes = 0;
1223
1224         /* get any errors */
1225         if (!This->bFlushing && !GetOverlappedResult(This->hFile, &pDataRq->ovl, &dwBytes, FALSE))
1226             hr = HRESULT_FROM_WIN32(GetLastError());
1227
1228         /* Return the sample no matter what so it can be destroyed */
1229         *ppSample = pDataRq->pSample;
1230         *pdwUser = pDataRq->dwUserData;
1231
1232         if (This->bFlushing)
1233             hr = VFW_E_WRONG_STATE;
1234
1235         if (FAILED(hr))
1236             dwBytes = 0;
1237
1238         /* Set the time on the sample */
1239         IMediaSample_SetActualDataLength(pDataRq->pSample, dwBytes);
1240
1241         rtStart = (DWORD64)pDataRq->ovl.u.s.Offset + ((DWORD64)pDataRq->ovl.u.s.OffsetHigh << 32);
1242         rtStart = MEDIATIME_FROM_BYTES(rtStart);
1243         rtStop = rtStart + MEDIATIME_FROM_BYTES(dwBytes);
1244
1245         IMediaSample_GetTime(pDataRq->pSample, &rtSampleStart, &rtSampleStop);
1246         assert(rtStart == rtSampleStart);
1247         assert(rtStop <= rtSampleStop);
1248
1249         IMediaSample_SetTime(pDataRq->pSample, &rtStart, &rtStop);
1250         assert(rtStart == rtSampleStart);
1251         if (hr == S_OK)
1252             assert(rtStop == rtSampleStop);
1253         else
1254             assert(rtStop == rtStart);
1255
1256         This->sample_list[buffer].pSample = NULL;
1257         assert(This->oldest_sample < This->samples);
1258
1259         if (buffer == This->oldest_sample)
1260         {
1261             LONG x;
1262             for (x = This->oldest_sample + 1; x < This->samples; ++x)
1263                 if (This->sample_list[x].pSample)
1264                     break;
1265             if (x >= This->samples)
1266                 for (x = 0; x < This->oldest_sample; ++x)
1267                     if (This->sample_list[x].pSample)
1268                         break;
1269             if (This->oldest_sample == x)
1270                 /* No samples found, reset to 0 */
1271                 x = 0;
1272             This->oldest_sample = x;
1273         }
1274     }
1275     LeaveCriticalSection(&This->csList);
1276
1277     TRACE("-- %x\n", hr);
1278     return hr;
1279 }
1280
1281 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer);
1282
1283 static HRESULT WINAPI FileAsyncReader_SyncReadAligned(IAsyncReader * iface, IMediaSample * pSample)
1284 {
1285     BYTE * pBuffer;
1286     REFERENCE_TIME tStart;
1287     REFERENCE_TIME tStop;
1288     HRESULT hr;
1289
1290     TRACE("(%p)\n", pSample);
1291
1292     hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
1293
1294     if (SUCCEEDED(hr))
1295         hr = IMediaSample_GetPointer(pSample, &pBuffer);
1296
1297     if (SUCCEEDED(hr))
1298         hr = FileAsyncReader_SyncRead(iface, 
1299             BYTES_FROM_MEDIATIME(tStart),
1300             (LONG) BYTES_FROM_MEDIATIME(tStop - tStart),
1301             pBuffer);
1302
1303     TRACE("-- %x\n", hr);
1304     return hr;
1305 }
1306
1307 static HRESULT WINAPI FileAsyncReader_SyncRead(IAsyncReader * iface, LONGLONG llPosition, LONG lLength, BYTE * pBuffer)
1308 {
1309     OVERLAPPED ovl;
1310     HRESULT hr = S_OK;
1311     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1312
1313     TRACE("(%x%08x, %d, %p)\n", (ULONG)(llPosition >> 32), (ULONG)llPosition, lLength, pBuffer);
1314
1315     ZeroMemory(&ovl, sizeof(ovl));
1316
1317     ovl.hEvent = CreateEventW(NULL, 0, 0, NULL);
1318     /* NOTE: llPosition is the actual byte position to start reading from */
1319     ovl.u.s.Offset = (DWORD) llPosition;
1320     ovl.u.s.OffsetHigh = (DWORD) (llPosition >> (sizeof(DWORD) * 8));
1321
1322     if (!ReadFile(This->hFile, pBuffer, lLength, NULL, &ovl))
1323         hr = HRESULT_FROM_WIN32(GetLastError());
1324
1325     if (hr == HRESULT_FROM_WIN32(ERROR_IO_PENDING))
1326         hr = S_OK;
1327
1328     if (SUCCEEDED(hr))
1329     {
1330         DWORD dwBytesRead;
1331
1332         if (!GetOverlappedResult(This->hFile, &ovl, &dwBytesRead, TRUE))
1333             hr = HRESULT_FROM_WIN32(GetLastError());
1334     }
1335
1336     CloseHandle(ovl.hEvent);
1337
1338     TRACE("-- %x\n", hr);
1339     return hr;
1340 }
1341
1342 static HRESULT WINAPI FileAsyncReader_Length(IAsyncReader * iface, LONGLONG * pTotal, LONGLONG * pAvailable)
1343 {
1344     DWORD dwSizeLow;
1345     DWORD dwSizeHigh;
1346     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1347
1348     TRACE("(%p, %p)\n", pTotal, pAvailable);
1349
1350     if (((dwSizeLow = GetFileSize(This->hFile, &dwSizeHigh)) == -1) &&
1351         (GetLastError() != NO_ERROR))
1352         return HRESULT_FROM_WIN32(GetLastError());
1353
1354     *pTotal = (LONGLONG)dwSizeLow | (LONGLONG)dwSizeHigh << (sizeof(DWORD) * 8);
1355
1356     *pAvailable = *pTotal;
1357
1358     return S_OK;
1359 }
1360
1361 static HRESULT WINAPI FileAsyncReader_BeginFlush(IAsyncReader * iface)
1362 {
1363     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1364
1365     TRACE("()\n");
1366
1367     EnterCriticalSection(&This->csList);
1368     This->bFlushing = TRUE;
1369     CancelIo(This->hFile);
1370     SetEvent(This->handle_list[This->samples]);
1371     LeaveCriticalSection(&This->csList);
1372
1373     return S_OK;
1374 }
1375
1376 static HRESULT WINAPI FileAsyncReader_EndFlush(IAsyncReader * iface)
1377 {
1378     FileAsyncReader *This = impl_from_IAsyncReader(iface);
1379     int x;
1380
1381     TRACE("()\n");
1382
1383     EnterCriticalSection(&This->csList);
1384     ResetEvent(This->handle_list[This->samples]);
1385     This->bFlushing = FALSE;
1386     for (x = 0; x < This->samples; ++x)
1387         assert(!This->sample_list[x].pSample);
1388
1389     LeaveCriticalSection(&This->csList);
1390
1391     return S_OK;
1392 }
1393
1394 static const IAsyncReaderVtbl FileAsyncReader_Vtbl = 
1395 {
1396     FileAsyncReader_QueryInterface,
1397     FileAsyncReader_AddRef,
1398     FileAsyncReader_Release,
1399     FileAsyncReader_RequestAllocator,
1400     FileAsyncReader_Request,
1401     FileAsyncReader_WaitForNext,
1402     FileAsyncReader_SyncReadAligned,
1403     FileAsyncReader_SyncRead,
1404     FileAsyncReader_Length,
1405     FileAsyncReader_BeginFlush,
1406     FileAsyncReader_EndFlush,
1407 };