msvcrt: Return child exit code in _pclose function.
[wine] / dlls / windowscodecs / stream.c
1 /*
2  * Copyright 2009 Tony Wasserka
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include "wine/debug.h"
20
21 #define COBJMACROS
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winreg.h"
25 #include "objbase.h"
26 #include "shlwapi.h"
27 #include "wincodec.h"
28 #include "wincodecs_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
31
32 /******************************************
33  * StreamOnMemory implementation
34  *
35  * Used by IWICStream_InitializeFromMemory
36  *
37  */
38 typedef struct StreamOnMemory {
39     IStream IStream_iface;
40     LONG ref;
41
42     BYTE *pbMemory;
43     DWORD dwMemsize;
44     DWORD dwCurPos;
45
46     CRITICAL_SECTION lock; /* must be held when pbMemory or dwCurPos is accessed */
47 } StreamOnMemory;
48
49 static inline StreamOnMemory *StreamOnMemory_from_IStream(IStream *iface)
50 {
51     return CONTAINING_RECORD(iface, StreamOnMemory, IStream_iface);
52 }
53
54 static HRESULT WINAPI StreamOnMemory_QueryInterface(IStream *iface,
55     REFIID iid, void **ppv)
56 {
57     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
58
59     if (!ppv) return E_INVALIDARG;
60
61     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
62         IsEqualIID(&IID_ISequentialStream, iid))
63     {
64         *ppv = iface;
65         IUnknown_AddRef((IUnknown*)*ppv);
66         return S_OK;
67     }
68     else
69     {
70         *ppv = NULL;
71         return E_NOINTERFACE;
72     }
73 }
74
75 static ULONG WINAPI StreamOnMemory_AddRef(IStream *iface)
76 {
77     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
78     ULONG ref = InterlockedIncrement(&This->ref);
79
80     TRACE("(%p) refcount=%u\n", iface, ref);
81
82     return ref;
83 }
84
85 static ULONG WINAPI StreamOnMemory_Release(IStream *iface)
86 {
87     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
88     ULONG ref = InterlockedDecrement(&This->ref);
89
90     TRACE("(%p) refcount=%u\n", iface, ref);
91
92     if (ref == 0) {
93         This->lock.DebugInfo->Spare[0] = 0;
94         DeleteCriticalSection(&This->lock);
95         HeapFree(GetProcessHeap(), 0, This);
96     }
97     return ref;
98 }
99
100 static HRESULT WINAPI StreamOnMemory_Read(IStream *iface,
101     void *pv, ULONG cb, ULONG *pcbRead)
102 {
103     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
104     ULONG uBytesRead;
105     TRACE("(%p)\n", This);
106
107     if (!pv) return E_INVALIDARG;
108
109     EnterCriticalSection(&This->lock);
110     uBytesRead = min(cb, This->dwMemsize - This->dwCurPos);
111     memmove(pv, This->pbMemory + This->dwCurPos, uBytesRead);
112     This->dwCurPos += uBytesRead;
113     LeaveCriticalSection(&This->lock);
114
115     if (pcbRead) *pcbRead = uBytesRead;
116
117     return S_OK;
118 }
119
120 static HRESULT WINAPI StreamOnMemory_Write(IStream *iface,
121     void const *pv, ULONG cb, ULONG *pcbWritten)
122 {
123     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
124     HRESULT hr;
125     TRACE("(%p)\n", This);
126
127     if (!pv) return E_INVALIDARG;
128
129     EnterCriticalSection(&This->lock);
130     if (cb > This->dwMemsize - This->dwCurPos) {
131         hr = STG_E_MEDIUMFULL;
132     }
133     else {
134         memmove(This->pbMemory + This->dwCurPos, pv, cb);
135         This->dwCurPos += cb;
136         hr = S_OK;
137         if (pcbWritten) *pcbWritten = cb;
138     }
139     LeaveCriticalSection(&This->lock);
140
141     return hr;
142 }
143
144 static HRESULT WINAPI StreamOnMemory_Seek(IStream *iface,
145     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
146 {
147     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
148     LARGE_INTEGER NewPosition;
149     HRESULT hr=S_OK;
150     TRACE("(%p)\n", This);
151
152     EnterCriticalSection(&This->lock);
153     if (dwOrigin == STREAM_SEEK_SET) NewPosition.QuadPart = dlibMove.QuadPart;
154     else if (dwOrigin == STREAM_SEEK_CUR) NewPosition.QuadPart = This->dwCurPos + dlibMove.QuadPart;
155     else if (dwOrigin == STREAM_SEEK_END) NewPosition.QuadPart = This->dwMemsize + dlibMove.QuadPart;
156     else hr = E_INVALIDARG;
157
158     if (SUCCEEDED(hr)) {
159         if (NewPosition.u.HighPart) hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
160         else if (NewPosition.QuadPart > This->dwMemsize) hr = E_INVALIDARG;
161         else if (NewPosition.QuadPart < 0) hr = E_INVALIDARG;
162     }
163
164     if (SUCCEEDED(hr)) {
165         This->dwCurPos = NewPosition.u.LowPart;
166
167         if(plibNewPosition) plibNewPosition->QuadPart = This->dwCurPos;
168     }
169     LeaveCriticalSection(&This->lock);
170
171     return hr;
172 }
173
174 /* SetSize isn't implemented in the native windowscodecs DLL either */
175 static HRESULT WINAPI StreamOnMemory_SetSize(IStream *iface,
176     ULARGE_INTEGER libNewSize)
177 {
178     TRACE("(%p)\n", iface);
179     return E_NOTIMPL;
180 }
181
182 /* CopyTo isn't implemented in the native windowscodecs DLL either */
183 static HRESULT WINAPI StreamOnMemory_CopyTo(IStream *iface,
184     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
185 {
186     TRACE("(%p)\n", iface);
187     return E_NOTIMPL;
188 }
189
190 /* Commit isn't implemented in the native windowscodecs DLL either */
191 static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface,
192     DWORD grfCommitFlags)
193 {
194     TRACE("(%p)\n", iface);
195     return E_NOTIMPL;
196 }
197
198 /* Revert isn't implemented in the native windowscodecs DLL either */
199 static HRESULT WINAPI StreamOnMemory_Revert(IStream *iface)
200 {
201     TRACE("(%p)\n", iface);
202     return E_NOTIMPL;
203 }
204
205 /* LockRegion isn't implemented in the native windowscodecs DLL either */
206 static HRESULT WINAPI StreamOnMemory_LockRegion(IStream *iface,
207     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
208 {
209     TRACE("(%p)\n", iface);
210     return E_NOTIMPL;
211 }
212
213 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
214 static HRESULT WINAPI StreamOnMemory_UnlockRegion(IStream *iface,
215     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
216 {
217     TRACE("(%p)\n", iface);
218     return E_NOTIMPL;
219 }
220
221 static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface,
222     STATSTG *pstatstg, DWORD grfStatFlag)
223 {
224     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
225     TRACE("(%p)\n", This);
226
227     if (!pstatstg) return E_INVALIDARG;
228
229     ZeroMemory(pstatstg, sizeof(STATSTG));
230     pstatstg->type = STGTY_STREAM;
231     pstatstg->cbSize.QuadPart = This->dwMemsize;
232
233     return S_OK;
234 }
235
236 /* Clone isn't implemented in the native windowscodecs DLL either */
237 static HRESULT WINAPI StreamOnMemory_Clone(IStream *iface,
238     IStream **ppstm)
239 {
240     TRACE("(%p)\n", iface);
241     return E_NOTIMPL;
242 }
243
244
245 static const IStreamVtbl StreamOnMemory_Vtbl =
246 {
247     /*** IUnknown methods ***/
248     StreamOnMemory_QueryInterface,
249     StreamOnMemory_AddRef,
250     StreamOnMemory_Release,
251     /*** ISequentialStream methods ***/
252     StreamOnMemory_Read,
253     StreamOnMemory_Write,
254     /*** IStream methods ***/
255     StreamOnMemory_Seek,
256     StreamOnMemory_SetSize,
257     StreamOnMemory_CopyTo,
258     StreamOnMemory_Commit,
259     StreamOnMemory_Revert,
260     StreamOnMemory_LockRegion,
261     StreamOnMemory_UnlockRegion,
262     StreamOnMemory_Stat,
263     StreamOnMemory_Clone,
264 };
265
266 /******************************************
267  * StreamOnStreamRange implementation
268  *
269  * Used by IWICStream_InitializeFromIStreamRegion
270  *
271  */
272 typedef struct StreamOnStreamRange {
273     IStream IStream_iface;
274     LONG ref;
275
276     IStream *stream;
277     ULARGE_INTEGER pos;
278     ULARGE_INTEGER offset;
279     ULARGE_INTEGER max_size;
280
281     CRITICAL_SECTION lock;
282 } StreamOnStreamRange;
283
284 static inline StreamOnStreamRange *StreamOnStreamRange_from_IStream(IStream *iface)
285 {
286     return CONTAINING_RECORD(iface, StreamOnStreamRange, IStream_iface);
287 }
288
289 static HRESULT WINAPI StreamOnStreamRange_QueryInterface(IStream *iface,
290     REFIID iid, void **ppv)
291 {
292     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
293
294     if (!ppv) return E_INVALIDARG;
295
296     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
297         IsEqualIID(&IID_ISequentialStream, iid))
298     {
299         *ppv = iface;
300         IUnknown_AddRef((IUnknown*)*ppv);
301         return S_OK;
302     }
303     else
304     {
305         *ppv = NULL;
306         return E_NOINTERFACE;
307     }
308 }
309
310 static ULONG WINAPI StreamOnStreamRange_AddRef(IStream *iface)
311 {
312     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
313     ULONG ref = InterlockedIncrement(&This->ref);
314
315     TRACE("(%p) refcount=%u\n", iface, ref);
316
317     return ref;
318 }
319
320 static ULONG WINAPI StreamOnStreamRange_Release(IStream *iface)
321 {
322     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
323     ULONG ref = InterlockedDecrement(&This->ref);
324
325     TRACE("(%p) refcount=%u\n", iface, ref);
326
327     if (ref == 0) {
328         This->lock.DebugInfo->Spare[0] = 0;
329         DeleteCriticalSection(&This->lock);
330         IStream_Release(This->stream);
331         HeapFree(GetProcessHeap(), 0, This);
332     }
333     return ref;
334 }
335
336 static HRESULT WINAPI StreamOnStreamRange_Read(IStream *iface,
337     void *pv, ULONG cb, ULONG *pcbRead)
338 {
339     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
340     ULONG uBytesRead=0;
341     HRESULT hr;
342     ULARGE_INTEGER OldPosition;
343     LARGE_INTEGER SetPosition;
344     TRACE("(%p)\n", This);
345
346     if (!pv) return E_INVALIDARG;
347
348     EnterCriticalSection(&This->lock);
349     SetPosition.QuadPart = 0;
350     hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition);
351     if (SUCCEEDED(hr))
352     {
353         SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart;
354         hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
355     }
356     if (SUCCEEDED(hr))
357     {
358         if (This->pos.QuadPart + cb > This->max_size.QuadPart)
359         {
360             /* This would read past the end of the stream. */
361             if (This->pos.QuadPart > This->max_size.QuadPart)
362                 cb = 0;
363             else
364                 cb = This->max_size.QuadPart - This->pos.QuadPart;
365         }
366         hr = IStream_Read(This->stream, pv, cb, &uBytesRead);
367         SetPosition.QuadPart = OldPosition.QuadPart;
368         IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
369     }
370     if (SUCCEEDED(hr))
371         This->pos.QuadPart += uBytesRead;
372     LeaveCriticalSection(&This->lock);
373
374     if (SUCCEEDED(hr) && pcbRead) *pcbRead = uBytesRead;
375
376     return hr;
377 }
378
379 static HRESULT WINAPI StreamOnStreamRange_Write(IStream *iface,
380     void const *pv, ULONG cb, ULONG *pcbWritten)
381 {
382     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
383     HRESULT hr;
384     ULARGE_INTEGER OldPosition;
385     LARGE_INTEGER SetPosition;
386     ULONG uBytesWritten=0;
387     TRACE("(%p)\n", This);
388
389     if (!pv) return E_INVALIDARG;
390
391     EnterCriticalSection(&This->lock);
392     SetPosition.QuadPart = 0;
393     hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition);
394     if (SUCCEEDED(hr))
395     {
396         SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart;
397         hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
398     }
399     if (SUCCEEDED(hr))
400     {
401         if (This->pos.QuadPart + cb > This->max_size.QuadPart)
402         {
403             /* This would read past the end of the stream. */
404             if (This->pos.QuadPart > This->max_size.QuadPart)
405                 cb = 0;
406             else
407                 cb = This->max_size.QuadPart - This->pos.QuadPart;
408         }
409         hr = IStream_Write(This->stream, pv, cb, &uBytesWritten);
410         SetPosition.QuadPart = OldPosition.QuadPart;
411         IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
412     }
413     if (SUCCEEDED(hr))
414         This->pos.QuadPart += uBytesWritten;
415     LeaveCriticalSection(&This->lock);
416
417     if (SUCCEEDED(hr) && pcbWritten) *pcbWritten = uBytesWritten;
418
419     return hr;
420 }
421
422 static HRESULT WINAPI StreamOnStreamRange_Seek(IStream *iface,
423     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
424 {
425     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
426     ULARGE_INTEGER NewPosition, actual_size;
427     HRESULT hr=S_OK;
428     STATSTG statstg;
429     TRACE("(%p)\n", This);
430
431     EnterCriticalSection(&This->lock);
432     actual_size = This->max_size;
433     if (dwOrigin == STREAM_SEEK_SET)
434         NewPosition.QuadPart = dlibMove.QuadPart;
435     else if (dwOrigin == STREAM_SEEK_CUR)
436         NewPosition.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
437     else if (dwOrigin == STREAM_SEEK_END)
438     {
439         hr = IStream_Stat(This->stream, &statstg, STATFLAG_NONAME);
440         if (SUCCEEDED(hr))
441         {
442             if (This->max_size.QuadPart + This->offset.QuadPart > statstg.cbSize.QuadPart)
443                 actual_size.QuadPart = statstg.cbSize.QuadPart - This->offset.QuadPart;
444             NewPosition.QuadPart = dlibMove.QuadPart + actual_size.QuadPart;
445         }
446     }
447     else hr = E_INVALIDARG;
448
449     if (SUCCEEDED(hr) && (NewPosition.u.HighPart != 0 || NewPosition.QuadPart > actual_size.QuadPart))
450         hr = WINCODEC_ERR_VALUEOUTOFRANGE;
451
452     if (SUCCEEDED(hr)) {
453         This->pos.QuadPart = NewPosition.QuadPart;
454
455         if(plibNewPosition) plibNewPosition->QuadPart = This->pos.QuadPart;
456     }
457     LeaveCriticalSection(&This->lock);
458
459     return hr;
460 }
461
462 /* SetSize isn't implemented in the native windowscodecs DLL either */
463 static HRESULT WINAPI StreamOnStreamRange_SetSize(IStream *iface,
464     ULARGE_INTEGER libNewSize)
465 {
466     TRACE("(%p)\n", iface);
467     return E_NOTIMPL;
468 }
469
470 /* CopyTo isn't implemented in the native windowscodecs DLL either */
471 static HRESULT WINAPI StreamOnStreamRange_CopyTo(IStream *iface,
472     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
473 {
474     TRACE("(%p)\n", iface);
475     return E_NOTIMPL;
476 }
477
478 /* Commit isn't implemented in the native windowscodecs DLL either */
479 static HRESULT WINAPI StreamOnStreamRange_Commit(IStream *iface,
480     DWORD grfCommitFlags)
481 {
482     TRACE("(%p)\n", iface);
483     return E_NOTIMPL;
484 }
485
486 /* Revert isn't implemented in the native windowscodecs DLL either */
487 static HRESULT WINAPI StreamOnStreamRange_Revert(IStream *iface)
488 {
489     TRACE("(%p)\n", iface);
490     return E_NOTIMPL;
491 }
492
493 /* LockRegion isn't implemented in the native windowscodecs DLL either */
494 static HRESULT WINAPI StreamOnStreamRange_LockRegion(IStream *iface,
495     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
496 {
497     TRACE("(%p)\n", iface);
498     return E_NOTIMPL;
499 }
500
501 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
502 static HRESULT WINAPI StreamOnStreamRange_UnlockRegion(IStream *iface,
503     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
504 {
505     TRACE("(%p)\n", iface);
506     return E_NOTIMPL;
507 }
508
509 static HRESULT WINAPI StreamOnStreamRange_Stat(IStream *iface,
510     STATSTG *pstatstg, DWORD grfStatFlag)
511 {
512     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
513     HRESULT hr;
514     TRACE("(%p)\n", This);
515
516     if (!pstatstg) return E_INVALIDARG;
517
518     EnterCriticalSection(&This->lock);
519     hr = IStream_Stat(This->stream, pstatstg, grfStatFlag);
520     if (SUCCEEDED(hr))
521     {
522         pstatstg->cbSize.QuadPart -= This->offset.QuadPart;
523         if (This->max_size.QuadPart < pstatstg->cbSize.QuadPart)
524             pstatstg->cbSize.QuadPart = This->max_size.QuadPart;
525     }
526
527     LeaveCriticalSection(&This->lock);
528
529     return hr;
530 }
531
532 /* Clone isn't implemented in the native windowscodecs DLL either */
533 static HRESULT WINAPI StreamOnStreamRange_Clone(IStream *iface,
534     IStream **ppstm)
535 {
536     TRACE("(%p)\n", iface);
537     return E_NOTIMPL;
538 }
539
540
541 static const IStreamVtbl StreamOnStreamRange_Vtbl =
542 {
543     /*** IUnknown methods ***/
544     StreamOnStreamRange_QueryInterface,
545     StreamOnStreamRange_AddRef,
546     StreamOnStreamRange_Release,
547     /*** ISequentialStream methods ***/
548     StreamOnStreamRange_Read,
549     StreamOnStreamRange_Write,
550     /*** IStream methods ***/
551     StreamOnStreamRange_Seek,
552     StreamOnStreamRange_SetSize,
553     StreamOnStreamRange_CopyTo,
554     StreamOnStreamRange_Commit,
555     StreamOnStreamRange_Revert,
556     StreamOnStreamRange_LockRegion,
557     StreamOnStreamRange_UnlockRegion,
558     StreamOnStreamRange_Stat,
559     StreamOnStreamRange_Clone,
560 };
561
562
563 /******************************************
564  * IWICStream implementation
565  *
566  */
567 typedef struct IWICStreamImpl
568 {
569     IWICStream IWICStream_iface;
570     LONG ref;
571
572     IStream *pStream;
573 } IWICStreamImpl;
574
575 static inline IWICStreamImpl *impl_from_IWICStream(IWICStream *iface)
576 {
577     return CONTAINING_RECORD(iface, IWICStreamImpl, IWICStream_iface);
578 }
579
580 static HRESULT WINAPI IWICStreamImpl_QueryInterface(IWICStream *iface,
581     REFIID iid, void **ppv)
582 {
583     IWICStreamImpl *This = impl_from_IWICStream(iface);
584     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
585
586     if (!ppv) return E_INVALIDARG;
587
588     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
589         IsEqualIID(&IID_ISequentialStream, iid) || IsEqualIID(&IID_IWICStream, iid))
590     {
591         *ppv = This;
592         IUnknown_AddRef((IUnknown*)*ppv);
593         return S_OK;
594     }
595     else
596     {
597         *ppv = NULL;
598         return E_NOINTERFACE;
599     }
600 }
601
602 static ULONG WINAPI IWICStreamImpl_AddRef(IWICStream *iface)
603 {
604     IWICStreamImpl *This = impl_from_IWICStream(iface);
605     ULONG ref = InterlockedIncrement(&This->ref);
606
607     TRACE("(%p) refcount=%u\n", iface, ref);
608
609     return ref;
610 }
611
612 static ULONG WINAPI IWICStreamImpl_Release(IWICStream *iface)
613 {
614     IWICStreamImpl *This = impl_from_IWICStream(iface);
615     ULONG ref = InterlockedDecrement(&This->ref);
616
617     TRACE("(%p) refcount=%u\n", iface, ref);
618
619     if (ref == 0) {
620         if (This->pStream) IStream_Release(This->pStream);
621         HeapFree(GetProcessHeap(), 0, This);
622     }
623     return ref;
624 }
625
626 static HRESULT WINAPI IWICStreamImpl_Read(IWICStream *iface,
627     void *pv, ULONG cb, ULONG *pcbRead)
628 {
629     IWICStreamImpl *This = impl_from_IWICStream(iface);
630     TRACE("(%p): relay\n", This);
631
632     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
633     return IStream_Read(This->pStream, pv, cb, pcbRead);
634 }
635
636 static HRESULT WINAPI IWICStreamImpl_Write(IWICStream *iface,
637     void const *pv, ULONG cb, ULONG *pcbWritten)
638 {
639     IWICStreamImpl *This = impl_from_IWICStream(iface);
640     TRACE("(%p): relay\n", This);
641
642     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
643     return IStream_Write(This->pStream, pv, cb, pcbWritten);
644 }
645
646 static HRESULT WINAPI IWICStreamImpl_Seek(IWICStream *iface,
647     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
648 {
649     IWICStreamImpl *This = impl_from_IWICStream(iface);
650     TRACE("(%p): relay\n", This);
651
652     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
653     return IStream_Seek(This->pStream, dlibMove, dwOrigin, plibNewPosition);
654 }
655
656 static HRESULT WINAPI IWICStreamImpl_SetSize(IWICStream *iface,
657     ULARGE_INTEGER libNewSize)
658 {
659     IWICStreamImpl *This = impl_from_IWICStream(iface);
660     TRACE("(%p): relay\n", This);
661
662     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
663     return IStream_SetSize(This->pStream, libNewSize);
664 }
665
666 static HRESULT WINAPI IWICStreamImpl_CopyTo(IWICStream *iface,
667     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
668 {
669     IWICStreamImpl *This = impl_from_IWICStream(iface);
670     TRACE("(%p): relay\n", This);
671
672     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
673     return IStream_CopyTo(This->pStream, pstm, cb, pcbRead, pcbWritten);
674 }
675
676 static HRESULT WINAPI IWICStreamImpl_Commit(IWICStream *iface,
677     DWORD grfCommitFlags)
678 {
679     IWICStreamImpl *This = impl_from_IWICStream(iface);
680     TRACE("(%p): relay\n", This);
681
682     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
683     return IStream_Commit(This->pStream, grfCommitFlags);
684 }
685
686 static HRESULT WINAPI IWICStreamImpl_Revert(IWICStream *iface)
687 {
688     IWICStreamImpl *This = impl_from_IWICStream(iface);
689     TRACE("(%p): relay\n", This);
690
691     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
692     return IStream_Revert(This->pStream);
693 }
694
695 static HRESULT WINAPI IWICStreamImpl_LockRegion(IWICStream *iface,
696     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
697 {
698     IWICStreamImpl *This = impl_from_IWICStream(iface);
699     TRACE("(%p): relay\n", This);
700
701     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
702     return IStream_LockRegion(This->pStream, libOffset, cb, dwLockType);
703 }
704
705 static HRESULT WINAPI IWICStreamImpl_UnlockRegion(IWICStream *iface,
706     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
707 {
708     IWICStreamImpl *This = impl_from_IWICStream(iface);
709     TRACE("(%p): relay\n", This);
710
711     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
712     return IStream_UnlockRegion(This->pStream, libOffset, cb, dwLockType);
713 }
714
715 static HRESULT WINAPI IWICStreamImpl_Stat(IWICStream *iface,
716     STATSTG *pstatstg, DWORD grfStatFlag)
717 {
718     IWICStreamImpl *This = impl_from_IWICStream(iface);
719     TRACE("(%p): relay\n", This);
720
721     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
722     return IStream_Stat(This->pStream, pstatstg, grfStatFlag);
723 }
724
725 static HRESULT WINAPI IWICStreamImpl_Clone(IWICStream *iface,
726     IStream **ppstm)
727 {
728     IWICStreamImpl *This = impl_from_IWICStream(iface);
729     TRACE("(%p): relay\n", This);
730
731     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
732     return IStream_Clone(This->pStream, ppstm);
733 }
734
735 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface,
736     IStream *pIStream)
737 {
738     ULARGE_INTEGER offset, size;
739     TRACE("(%p): relay\n", iface);
740
741     offset.QuadPart = 0;
742     size.u.LowPart = 0xffffffff;
743     size.u.HighPart = 0xffffffff;
744     return IWICStream_InitializeFromIStreamRegion(iface, pIStream, offset, size);
745 }
746
747 static HRESULT WINAPI IWICStreamImpl_InitializeFromFilename(IWICStream *iface,
748     LPCWSTR wzFileName, DWORD dwDesiredAccess)
749 {
750     IWICStreamImpl *This = impl_from_IWICStream(iface);
751     HRESULT hr;
752     DWORD dwMode;
753     IStream *stream;
754
755     TRACE("(%p, %s, %u)\n", iface, debugstr_w(wzFileName), dwDesiredAccess);
756
757     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
758
759     if(dwDesiredAccess & GENERIC_WRITE)
760         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
761     else if(dwDesiredAccess & GENERIC_READ)
762         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
763     else
764         return E_INVALIDARG;
765
766     hr = SHCreateStreamOnFileW(wzFileName, dwMode, &stream);
767
768     if (SUCCEEDED(hr))
769     {
770         if (InterlockedCompareExchangePointer((void**)&This->pStream, stream, NULL))
771         {
772             /* Some other thread set the stream first. */
773             IStream_Release(stream);
774             hr = WINCODEC_ERR_WRONGSTATE;
775         }
776     }
777
778     return hr;
779 }
780
781 /******************************************
782  * IWICStream_InitializeFromMemory
783  *
784  * Initializes the internal IStream object to retrieve its data from a memory chunk.
785  *
786  * PARAMS
787  *   pbBuffer     [I] pointer to the memory chunk
788  *   cbBufferSize [I] number of bytes to use from the memory chunk
789  *
790  * RETURNS
791  *   SUCCESS: S_OK
792  *   FAILURE: E_INVALIDARG, if pbBuffer is NULL
793  *            E_OUTOFMEMORY, if we run out of memory
794  *            WINCODEC_ERR_WRONGSTATE, if the IStream object has already been initialized before
795  *
796  */
797 static HRESULT WINAPI IWICStreamImpl_InitializeFromMemory(IWICStream *iface,
798     BYTE *pbBuffer, DWORD cbBufferSize)
799 {
800     IWICStreamImpl *This = impl_from_IWICStream(iface);
801     StreamOnMemory *pObject;
802     TRACE("(%p,%p)\n", iface, pbBuffer);
803
804     if (!pbBuffer) return E_INVALIDARG;
805     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
806
807     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnMemory));
808     if (!pObject) return E_OUTOFMEMORY;
809
810     pObject->IStream_iface.lpVtbl = &StreamOnMemory_Vtbl;
811     pObject->ref = 1;
812     pObject->pbMemory = pbBuffer;
813     pObject->dwMemsize = cbBufferSize;
814     pObject->dwCurPos = 0;
815     InitializeCriticalSection(&pObject->lock);
816     pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnMemory.lock");
817
818     if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL))
819     {
820         /* Some other thread set the stream first. */
821         IStream_Release(&pObject->IStream_iface);
822         return WINCODEC_ERR_WRONGSTATE;
823     }
824
825     return S_OK;
826 }
827
828 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStreamRegion(IWICStream *iface,
829     IStream *pIStream, ULARGE_INTEGER ulOffset, ULARGE_INTEGER ulMaxSize)
830 {
831     IWICStreamImpl *This = impl_from_IWICStream(iface);
832     StreamOnStreamRange *pObject;
833     TRACE("(%p,%p)\n", iface, pIStream);
834
835     if (!pIStream) return E_INVALIDARG;
836     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
837
838     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnStreamRange));
839     if (!pObject) return E_OUTOFMEMORY;
840
841     pObject->IStream_iface.lpVtbl = &StreamOnStreamRange_Vtbl;
842     pObject->ref = 1;
843     IStream_AddRef(pIStream);
844     pObject->stream = pIStream;
845     pObject->pos.QuadPart = 0;
846     pObject->offset = ulOffset;
847     pObject->max_size = ulMaxSize;
848     InitializeCriticalSection(&pObject->lock);
849     pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnStreamRange.lock");
850
851     if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL))
852     {
853         /* Some other thread set the stream first. */
854         IStream_Release(&pObject->IStream_iface);
855         return WINCODEC_ERR_WRONGSTATE;
856     }
857
858     return S_OK;
859 }
860
861
862 static const IWICStreamVtbl WICStream_Vtbl =
863 {
864     /*** IUnknown methods ***/
865     IWICStreamImpl_QueryInterface,
866     IWICStreamImpl_AddRef,
867     IWICStreamImpl_Release,
868     /*** ISequentialStream methods ***/
869     IWICStreamImpl_Read,
870     IWICStreamImpl_Write,
871     /*** IStream methods ***/
872     IWICStreamImpl_Seek,
873     IWICStreamImpl_SetSize,
874     IWICStreamImpl_CopyTo,
875     IWICStreamImpl_Commit,
876     IWICStreamImpl_Revert,
877     IWICStreamImpl_LockRegion,
878     IWICStreamImpl_UnlockRegion,
879     IWICStreamImpl_Stat,
880     IWICStreamImpl_Clone,
881     /*** IWICStream methods ***/
882     IWICStreamImpl_InitializeFromIStream,
883     IWICStreamImpl_InitializeFromFilename,
884     IWICStreamImpl_InitializeFromMemory,
885     IWICStreamImpl_InitializeFromIStreamRegion,
886 };
887
888 HRESULT StreamImpl_Create(IWICStream **stream)
889 {
890     IWICStreamImpl *pObject;
891
892     if( !stream ) return E_INVALIDARG;
893
894     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(IWICStreamImpl));
895     if( !pObject ) {
896         *stream = NULL;
897         return E_OUTOFMEMORY;
898     }
899
900     pObject->IWICStream_iface.lpVtbl = &WICStream_Vtbl;
901     pObject->ref = 1;
902     pObject->pStream = NULL;
903
904     *stream = &pObject->IWICStream_iface;
905
906     return S_OK;
907 }