xref: /reactos/dll/win32/windowscodecs/stream.c (revision 845faec4)
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 "wincodecs_private.h"
28 
29 WINE_DEFAULT_DEBUG_CHANNEL(wincodecs);
30 
31 /******************************************
32  * StreamOnMemory implementation
33  *
34  * Used by IWICStream_InitializeFromMemory
35  *
36  */
37 typedef struct StreamOnMemory {
38     IStream IStream_iface;
39     LONG ref;
40 
41     BYTE *pbMemory;
42     DWORD dwMemsize;
43     DWORD dwCurPos;
44 
45     CRITICAL_SECTION lock; /* must be held when pbMemory or dwCurPos is accessed */
46 } StreamOnMemory;
47 
48 static inline StreamOnMemory *StreamOnMemory_from_IStream(IStream *iface)
49 {
50     return CONTAINING_RECORD(iface, StreamOnMemory, IStream_iface);
51 }
52 
53 static HRESULT WINAPI StreamOnMemory_QueryInterface(IStream *iface,
54     REFIID iid, void **ppv)
55 {
56     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
57 
58     if (!ppv) return E_INVALIDARG;
59 
60     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
61         IsEqualIID(&IID_ISequentialStream, iid))
62     {
63         *ppv = iface;
64         IUnknown_AddRef((IUnknown*)*ppv);
65         return S_OK;
66     }
67     else
68     {
69         *ppv = NULL;
70         return E_NOINTERFACE;
71     }
72 }
73 
74 static ULONG WINAPI StreamOnMemory_AddRef(IStream *iface)
75 {
76     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
77     ULONG ref = InterlockedIncrement(&This->ref);
78 
79     TRACE("(%p) refcount=%u\n", iface, ref);
80 
81     return ref;
82 }
83 
84 static ULONG WINAPI StreamOnMemory_Release(IStream *iface)
85 {
86     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
87     ULONG ref = InterlockedDecrement(&This->ref);
88 
89     TRACE("(%p) refcount=%u\n", iface, ref);
90 
91     if (ref == 0) {
92         This->lock.DebugInfo->Spare[0] = 0;
93         DeleteCriticalSection(&This->lock);
94         HeapFree(GetProcessHeap(), 0, This);
95     }
96     return ref;
97 }
98 
99 static HRESULT WINAPI StreamOnMemory_Read(IStream *iface,
100     void *pv, ULONG cb, ULONG *pcbRead)
101 {
102     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
103     ULONG uBytesRead;
104     TRACE("(%p)\n", This);
105 
106     if (!pv) return E_INVALIDARG;
107 
108     EnterCriticalSection(&This->lock);
109     uBytesRead = min(cb, This->dwMemsize - This->dwCurPos);
110     memmove(pv, This->pbMemory + This->dwCurPos, uBytesRead);
111     This->dwCurPos += uBytesRead;
112     LeaveCriticalSection(&This->lock);
113 
114     if (pcbRead) *pcbRead = uBytesRead;
115 
116     return S_OK;
117 }
118 
119 static HRESULT WINAPI StreamOnMemory_Write(IStream *iface,
120     void const *pv, ULONG cb, ULONG *pcbWritten)
121 {
122     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
123     HRESULT hr;
124     TRACE("(%p)\n", This);
125 
126     if (!pv) return E_INVALIDARG;
127 
128     EnterCriticalSection(&This->lock);
129     if (cb > This->dwMemsize - This->dwCurPos) {
130         hr = STG_E_MEDIUMFULL;
131     }
132     else {
133         memmove(This->pbMemory + This->dwCurPos, pv, cb);
134         This->dwCurPos += cb;
135         hr = S_OK;
136         if (pcbWritten) *pcbWritten = cb;
137     }
138     LeaveCriticalSection(&This->lock);
139 
140     return hr;
141 }
142 
143 static HRESULT WINAPI StreamOnMemory_Seek(IStream *iface,
144     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
145 {
146     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
147     LARGE_INTEGER NewPosition;
148     HRESULT hr=S_OK;
149     TRACE("(%p)\n", This);
150 
151     EnterCriticalSection(&This->lock);
152     if (dwOrigin == STREAM_SEEK_SET) NewPosition.QuadPart = dlibMove.QuadPart;
153     else if (dwOrigin == STREAM_SEEK_CUR) NewPosition.QuadPart = This->dwCurPos + dlibMove.QuadPart;
154     else if (dwOrigin == STREAM_SEEK_END) NewPosition.QuadPart = This->dwMemsize + dlibMove.QuadPart;
155     else hr = E_INVALIDARG;
156 
157     if (SUCCEEDED(hr)) {
158         if (NewPosition.u.HighPart) hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
159         else if (NewPosition.QuadPart > This->dwMemsize) hr = E_INVALIDARG;
160         else if (NewPosition.QuadPart < 0) hr = E_INVALIDARG;
161     }
162 
163     if (SUCCEEDED(hr)) {
164         This->dwCurPos = NewPosition.u.LowPart;
165 
166         if(plibNewPosition) plibNewPosition->QuadPart = This->dwCurPos;
167     }
168     LeaveCriticalSection(&This->lock);
169 
170     return hr;
171 }
172 
173 /* SetSize isn't implemented in the native windowscodecs DLL either */
174 static HRESULT WINAPI StreamOnMemory_SetSize(IStream *iface,
175     ULARGE_INTEGER libNewSize)
176 {
177     TRACE("(%p)\n", iface);
178     return E_NOTIMPL;
179 }
180 
181 /* CopyTo isn't implemented in the native windowscodecs DLL either */
182 static HRESULT WINAPI StreamOnMemory_CopyTo(IStream *iface,
183     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
184 {
185     TRACE("(%p)\n", iface);
186     return E_NOTIMPL;
187 }
188 
189 /* Commit isn't implemented in the native windowscodecs DLL either */
190 static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface,
191     DWORD grfCommitFlags)
192 {
193     TRACE("(%p)\n", iface);
194     return E_NOTIMPL;
195 }
196 
197 /* Revert isn't implemented in the native windowscodecs DLL either */
198 static HRESULT WINAPI StreamOnMemory_Revert(IStream *iface)
199 {
200     TRACE("(%p)\n", iface);
201     return E_NOTIMPL;
202 }
203 
204 /* LockRegion isn't implemented in the native windowscodecs DLL either */
205 static HRESULT WINAPI StreamOnMemory_LockRegion(IStream *iface,
206     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
207 {
208     TRACE("(%p)\n", iface);
209     return E_NOTIMPL;
210 }
211 
212 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
213 static HRESULT WINAPI StreamOnMemory_UnlockRegion(IStream *iface,
214     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
215 {
216     TRACE("(%p)\n", iface);
217     return E_NOTIMPL;
218 }
219 
220 static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface,
221     STATSTG *pstatstg, DWORD grfStatFlag)
222 {
223     StreamOnMemory *This = StreamOnMemory_from_IStream(iface);
224     TRACE("(%p)\n", This);
225 
226     if (!pstatstg) return E_INVALIDARG;
227 
228     ZeroMemory(pstatstg, sizeof(STATSTG));
229     pstatstg->type = STGTY_STREAM;
230     pstatstg->cbSize.QuadPart = This->dwMemsize;
231 
232     return S_OK;
233 }
234 
235 /* Clone isn't implemented in the native windowscodecs DLL either */
236 static HRESULT WINAPI StreamOnMemory_Clone(IStream *iface,
237     IStream **ppstm)
238 {
239     TRACE("(%p)\n", iface);
240     return E_NOTIMPL;
241 }
242 
243 
244 static const IStreamVtbl StreamOnMemory_Vtbl =
245 {
246     /*** IUnknown methods ***/
247     StreamOnMemory_QueryInterface,
248     StreamOnMemory_AddRef,
249     StreamOnMemory_Release,
250     /*** ISequentialStream methods ***/
251     StreamOnMemory_Read,
252     StreamOnMemory_Write,
253     /*** IStream methods ***/
254     StreamOnMemory_Seek,
255     StreamOnMemory_SetSize,
256     StreamOnMemory_CopyTo,
257     StreamOnMemory_Commit,
258     StreamOnMemory_Revert,
259     StreamOnMemory_LockRegion,
260     StreamOnMemory_UnlockRegion,
261     StreamOnMemory_Stat,
262     StreamOnMemory_Clone,
263 };
264 
265 /******************************************
266  * StreamOnFileHandle implementation (internal)
267  *
268  */
269 typedef struct StreamOnFileHandle {
270     IStream IStream_iface;
271     LONG ref;
272 
273     HANDLE map;
274     void *mem;
275     IWICStream *stream;
276 } StreamOnFileHandle;
277 
278 static inline StreamOnFileHandle *StreamOnFileHandle_from_IStream(IStream *iface)
279 {
280     return CONTAINING_RECORD(iface, StreamOnFileHandle, IStream_iface);
281 }
282 
283 static HRESULT WINAPI StreamOnFileHandle_QueryInterface(IStream *iface,
284     REFIID iid, void **ppv)
285 {
286     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
287 
288     if (!ppv) return E_INVALIDARG;
289 
290     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
291         IsEqualIID(&IID_ISequentialStream, iid))
292     {
293         *ppv = iface;
294         IUnknown_AddRef((IUnknown*)*ppv);
295         return S_OK;
296     }
297     else
298     {
299         *ppv = NULL;
300         return E_NOINTERFACE;
301     }
302 }
303 
304 static ULONG WINAPI StreamOnFileHandle_AddRef(IStream *iface)
305 {
306     StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
307     ULONG ref = InterlockedIncrement(&This->ref);
308 
309     TRACE("(%p) refcount=%u\n", iface, ref);
310 
311     return ref;
312 }
313 
314 static ULONG WINAPI StreamOnFileHandle_Release(IStream *iface)
315 {
316     StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
317     ULONG ref = InterlockedDecrement(&This->ref);
318 
319     TRACE("(%p) refcount=%u\n", iface, ref);
320 
321     if (ref == 0) {
322         IWICStream_Release(This->stream);
323         UnmapViewOfFile(This->mem);
324         CloseHandle(This->map);
325         HeapFree(GetProcessHeap(), 0, This);
326     }
327     return ref;
328 }
329 
330 static HRESULT WINAPI StreamOnFileHandle_Read(IStream *iface,
331     void *pv, ULONG cb, ULONG *pcbRead)
332 {
333     StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
334     TRACE("(%p)\n", This);
335 
336     return IWICStream_Read(This->stream, pv, cb, pcbRead);
337 }
338 
339 static HRESULT WINAPI StreamOnFileHandle_Write(IStream *iface,
340     void const *pv, ULONG cb, ULONG *pcbWritten)
341 {
342     ERR("(%p)\n", iface);
343     return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
344 }
345 
346 static HRESULT WINAPI StreamOnFileHandle_Seek(IStream *iface,
347     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
348 {
349     StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
350     TRACE("(%p)\n", This);
351 
352     return IWICStream_Seek(This->stream, dlibMove, dwOrigin, plibNewPosition);
353 }
354 
355 static HRESULT WINAPI StreamOnFileHandle_SetSize(IStream *iface,
356     ULARGE_INTEGER libNewSize)
357 {
358     TRACE("(%p)\n", iface);
359     return E_NOTIMPL;
360 }
361 
362 static HRESULT WINAPI StreamOnFileHandle_CopyTo(IStream *iface,
363     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
364 {
365     TRACE("(%p)\n", iface);
366     return E_NOTIMPL;
367 }
368 
369 static HRESULT WINAPI StreamOnFileHandle_Commit(IStream *iface,
370     DWORD grfCommitFlags)
371 {
372     TRACE("(%p)\n", iface);
373     return E_NOTIMPL;
374 }
375 
376 static HRESULT WINAPI StreamOnFileHandle_Revert(IStream *iface)
377 {
378     TRACE("(%p)\n", iface);
379     return E_NOTIMPL;
380 }
381 
382 static HRESULT WINAPI StreamOnFileHandle_LockRegion(IStream *iface,
383     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
384 {
385     TRACE("(%p)\n", iface);
386     return E_NOTIMPL;
387 }
388 
389 static HRESULT WINAPI StreamOnFileHandle_UnlockRegion(IStream *iface,
390     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
391 {
392     TRACE("(%p)\n", iface);
393     return E_NOTIMPL;
394 }
395 
396 static HRESULT WINAPI StreamOnFileHandle_Stat(IStream *iface,
397     STATSTG *pstatstg, DWORD grfStatFlag)
398 {
399     StreamOnFileHandle *This = StreamOnFileHandle_from_IStream(iface);
400     TRACE("(%p)\n", This);
401 
402     return IWICStream_Stat(This->stream, pstatstg, grfStatFlag);
403 }
404 
405 static HRESULT WINAPI StreamOnFileHandle_Clone(IStream *iface,
406     IStream **ppstm)
407 {
408     TRACE("(%p)\n", iface);
409     return E_NOTIMPL;
410 }
411 
412 
413 static const IStreamVtbl StreamOnFileHandle_Vtbl =
414 {
415     /*** IUnknown methods ***/
416     StreamOnFileHandle_QueryInterface,
417     StreamOnFileHandle_AddRef,
418     StreamOnFileHandle_Release,
419     /*** ISequentialStream methods ***/
420     StreamOnFileHandle_Read,
421     StreamOnFileHandle_Write,
422     /*** IStream methods ***/
423     StreamOnFileHandle_Seek,
424     StreamOnFileHandle_SetSize,
425     StreamOnFileHandle_CopyTo,
426     StreamOnFileHandle_Commit,
427     StreamOnFileHandle_Revert,
428     StreamOnFileHandle_LockRegion,
429     StreamOnFileHandle_UnlockRegion,
430     StreamOnFileHandle_Stat,
431     StreamOnFileHandle_Clone,
432 };
433 
434 /******************************************
435  * StreamOnStreamRange implementation
436  *
437  * Used by IWICStream_InitializeFromIStreamRegion
438  *
439  */
440 typedef struct StreamOnStreamRange {
441     IStream IStream_iface;
442     LONG ref;
443 
444     IStream *stream;
445     ULARGE_INTEGER pos;
446     ULARGE_INTEGER offset;
447     ULARGE_INTEGER max_size;
448 
449     CRITICAL_SECTION lock;
450 } StreamOnStreamRange;
451 
452 static inline StreamOnStreamRange *StreamOnStreamRange_from_IStream(IStream *iface)
453 {
454     return CONTAINING_RECORD(iface, StreamOnStreamRange, IStream_iface);
455 }
456 
457 static HRESULT WINAPI StreamOnStreamRange_QueryInterface(IStream *iface,
458     REFIID iid, void **ppv)
459 {
460     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
461 
462     if (!ppv) return E_INVALIDARG;
463 
464     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
465         IsEqualIID(&IID_ISequentialStream, iid))
466     {
467         *ppv = iface;
468         IUnknown_AddRef((IUnknown*)*ppv);
469         return S_OK;
470     }
471     else
472     {
473         *ppv = NULL;
474         return E_NOINTERFACE;
475     }
476 }
477 
478 static ULONG WINAPI StreamOnStreamRange_AddRef(IStream *iface)
479 {
480     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
481     ULONG ref = InterlockedIncrement(&This->ref);
482 
483     TRACE("(%p) refcount=%u\n", iface, ref);
484 
485     return ref;
486 }
487 
488 static ULONG WINAPI StreamOnStreamRange_Release(IStream *iface)
489 {
490     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
491     ULONG ref = InterlockedDecrement(&This->ref);
492 
493     TRACE("(%p) refcount=%u\n", iface, ref);
494 
495     if (ref == 0) {
496         This->lock.DebugInfo->Spare[0] = 0;
497         DeleteCriticalSection(&This->lock);
498         IStream_Release(This->stream);
499         HeapFree(GetProcessHeap(), 0, This);
500     }
501     return ref;
502 }
503 
504 static HRESULT WINAPI StreamOnStreamRange_Read(IStream *iface,
505     void *pv, ULONG cb, ULONG *pcbRead)
506 {
507     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
508     ULONG uBytesRead=0;
509     HRESULT hr;
510     ULARGE_INTEGER OldPosition;
511     LARGE_INTEGER SetPosition;
512     TRACE("(%p)\n", This);
513 
514     if (!pv) return E_INVALIDARG;
515 
516     EnterCriticalSection(&This->lock);
517     SetPosition.QuadPart = 0;
518     hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition);
519     if (SUCCEEDED(hr))
520     {
521         SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart;
522         hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
523     }
524     if (SUCCEEDED(hr))
525     {
526         if (This->pos.QuadPart + cb > This->max_size.QuadPart)
527         {
528             /* This would read past the end of the stream. */
529             if (This->pos.QuadPart > This->max_size.QuadPart)
530                 cb = 0;
531             else
532                 cb = This->max_size.QuadPart - This->pos.QuadPart;
533         }
534         hr = IStream_Read(This->stream, pv, cb, &uBytesRead);
535         SetPosition.QuadPart = OldPosition.QuadPart;
536         IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
537     }
538     if (SUCCEEDED(hr))
539         This->pos.QuadPart += uBytesRead;
540     LeaveCriticalSection(&This->lock);
541 
542     if (SUCCEEDED(hr) && pcbRead) *pcbRead = uBytesRead;
543 
544     return hr;
545 }
546 
547 static HRESULT WINAPI StreamOnStreamRange_Write(IStream *iface,
548     void const *pv, ULONG cb, ULONG *pcbWritten)
549 {
550     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
551     HRESULT hr;
552     ULARGE_INTEGER OldPosition;
553     LARGE_INTEGER SetPosition;
554     ULONG uBytesWritten=0;
555     TRACE("(%p)\n", This);
556 
557     if (!pv) return E_INVALIDARG;
558 
559     EnterCriticalSection(&This->lock);
560     SetPosition.QuadPart = 0;
561     hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_CUR, &OldPosition);
562     if (SUCCEEDED(hr))
563     {
564         SetPosition.QuadPart = This->pos.QuadPart + This->offset.QuadPart;
565         hr = IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
566     }
567     if (SUCCEEDED(hr))
568     {
569         if (This->pos.QuadPart + cb > This->max_size.QuadPart)
570         {
571             /* This would read past the end of the stream. */
572             if (This->pos.QuadPart > This->max_size.QuadPart)
573                 cb = 0;
574             else
575                 cb = This->max_size.QuadPart - This->pos.QuadPart;
576         }
577         hr = IStream_Write(This->stream, pv, cb, &uBytesWritten);
578         SetPosition.QuadPart = OldPosition.QuadPart;
579         IStream_Seek(This->stream, SetPosition, STREAM_SEEK_SET, NULL);
580     }
581     if (SUCCEEDED(hr))
582         This->pos.QuadPart += uBytesWritten;
583     LeaveCriticalSection(&This->lock);
584 
585     if (SUCCEEDED(hr) && pcbWritten) *pcbWritten = uBytesWritten;
586 
587     return hr;
588 }
589 
590 static HRESULT WINAPI StreamOnStreamRange_Seek(IStream *iface,
591     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
592 {
593     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
594     ULARGE_INTEGER NewPosition, actual_size;
595     HRESULT hr=S_OK;
596     STATSTG statstg;
597     TRACE("(%p)\n", This);
598 
599     EnterCriticalSection(&This->lock);
600     actual_size = This->max_size;
601     if (dwOrigin == STREAM_SEEK_SET)
602         NewPosition.QuadPart = dlibMove.QuadPart;
603     else if (dwOrigin == STREAM_SEEK_CUR)
604         NewPosition.QuadPart = This->pos.QuadPart + dlibMove.QuadPart;
605     else if (dwOrigin == STREAM_SEEK_END)
606     {
607         hr = IStream_Stat(This->stream, &statstg, STATFLAG_NONAME);
608         if (SUCCEEDED(hr))
609         {
610             if (This->max_size.QuadPart + This->offset.QuadPart > statstg.cbSize.QuadPart)
611                 actual_size.QuadPart = statstg.cbSize.QuadPart - This->offset.QuadPart;
612             NewPosition.QuadPart = dlibMove.QuadPart + actual_size.QuadPart;
613         }
614     }
615     else hr = E_INVALIDARG;
616 
617     if (SUCCEEDED(hr) && (NewPosition.u.HighPart != 0 || NewPosition.QuadPart > actual_size.QuadPart))
618         hr = WINCODEC_ERR_VALUEOUTOFRANGE;
619 
620     if (SUCCEEDED(hr)) {
621         This->pos.QuadPart = NewPosition.QuadPart;
622 
623         if(plibNewPosition) plibNewPosition->QuadPart = This->pos.QuadPart;
624     }
625     LeaveCriticalSection(&This->lock);
626 
627     return hr;
628 }
629 
630 /* SetSize isn't implemented in the native windowscodecs DLL either */
631 static HRESULT WINAPI StreamOnStreamRange_SetSize(IStream *iface,
632     ULARGE_INTEGER libNewSize)
633 {
634     TRACE("(%p)\n", iface);
635     return E_NOTIMPL;
636 }
637 
638 /* CopyTo isn't implemented in the native windowscodecs DLL either */
639 static HRESULT WINAPI StreamOnStreamRange_CopyTo(IStream *iface,
640     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
641 {
642     TRACE("(%p)\n", iface);
643     return E_NOTIMPL;
644 }
645 
646 /* Commit isn't implemented in the native windowscodecs DLL either */
647 static HRESULT WINAPI StreamOnStreamRange_Commit(IStream *iface,
648     DWORD grfCommitFlags)
649 {
650     TRACE("(%p)\n", iface);
651     return E_NOTIMPL;
652 }
653 
654 /* Revert isn't implemented in the native windowscodecs DLL either */
655 static HRESULT WINAPI StreamOnStreamRange_Revert(IStream *iface)
656 {
657     TRACE("(%p)\n", iface);
658     return E_NOTIMPL;
659 }
660 
661 /* LockRegion isn't implemented in the native windowscodecs DLL either */
662 static HRESULT WINAPI StreamOnStreamRange_LockRegion(IStream *iface,
663     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
664 {
665     TRACE("(%p)\n", iface);
666     return E_NOTIMPL;
667 }
668 
669 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
670 static HRESULT WINAPI StreamOnStreamRange_UnlockRegion(IStream *iface,
671     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
672 {
673     TRACE("(%p)\n", iface);
674     return E_NOTIMPL;
675 }
676 
677 static HRESULT WINAPI StreamOnStreamRange_Stat(IStream *iface,
678     STATSTG *pstatstg, DWORD grfStatFlag)
679 {
680     StreamOnStreamRange *This = StreamOnStreamRange_from_IStream(iface);
681     HRESULT hr;
682     TRACE("(%p)\n", This);
683 
684     if (!pstatstg) return E_INVALIDARG;
685 
686     EnterCriticalSection(&This->lock);
687     hr = IStream_Stat(This->stream, pstatstg, grfStatFlag);
688     if (SUCCEEDED(hr))
689     {
690         pstatstg->cbSize.QuadPart -= This->offset.QuadPart;
691         if (This->max_size.QuadPart < pstatstg->cbSize.QuadPart)
692             pstatstg->cbSize.QuadPart = This->max_size.QuadPart;
693     }
694 
695     LeaveCriticalSection(&This->lock);
696 
697     return hr;
698 }
699 
700 /* Clone isn't implemented in the native windowscodecs DLL either */
701 static HRESULT WINAPI StreamOnStreamRange_Clone(IStream *iface,
702     IStream **ppstm)
703 {
704     TRACE("(%p)\n", iface);
705     return E_NOTIMPL;
706 }
707 
708 
709 static const IStreamVtbl StreamOnStreamRange_Vtbl =
710 {
711     /*** IUnknown methods ***/
712     StreamOnStreamRange_QueryInterface,
713     StreamOnStreamRange_AddRef,
714     StreamOnStreamRange_Release,
715     /*** ISequentialStream methods ***/
716     StreamOnStreamRange_Read,
717     StreamOnStreamRange_Write,
718     /*** IStream methods ***/
719     StreamOnStreamRange_Seek,
720     StreamOnStreamRange_SetSize,
721     StreamOnStreamRange_CopyTo,
722     StreamOnStreamRange_Commit,
723     StreamOnStreamRange_Revert,
724     StreamOnStreamRange_LockRegion,
725     StreamOnStreamRange_UnlockRegion,
726     StreamOnStreamRange_Stat,
727     StreamOnStreamRange_Clone,
728 };
729 
730 
731 /******************************************
732  * IWICStream implementation
733  *
734  */
735 typedef struct IWICStreamImpl
736 {
737     IWICStream IWICStream_iface;
738     LONG ref;
739 
740     IStream *pStream;
741 } IWICStreamImpl;
742 
743 static inline IWICStreamImpl *impl_from_IWICStream(IWICStream *iface)
744 {
745     return CONTAINING_RECORD(iface, IWICStreamImpl, IWICStream_iface);
746 }
747 
748 static HRESULT WINAPI IWICStreamImpl_QueryInterface(IWICStream *iface,
749     REFIID iid, void **ppv)
750 {
751     IWICStreamImpl *This = impl_from_IWICStream(iface);
752     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
753 
754     if (!ppv) return E_INVALIDARG;
755 
756     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
757         IsEqualIID(&IID_ISequentialStream, iid) || IsEqualIID(&IID_IWICStream, iid))
758     {
759         *ppv = &This->IWICStream_iface;
760         IUnknown_AddRef((IUnknown*)*ppv);
761         return S_OK;
762     }
763     else
764     {
765         *ppv = NULL;
766         return E_NOINTERFACE;
767     }
768 }
769 
770 static ULONG WINAPI IWICStreamImpl_AddRef(IWICStream *iface)
771 {
772     IWICStreamImpl *This = impl_from_IWICStream(iface);
773     ULONG ref = InterlockedIncrement(&This->ref);
774 
775     TRACE("(%p) refcount=%u\n", iface, ref);
776 
777     return ref;
778 }
779 
780 static ULONG WINAPI IWICStreamImpl_Release(IWICStream *iface)
781 {
782     IWICStreamImpl *This = impl_from_IWICStream(iface);
783     ULONG ref = InterlockedDecrement(&This->ref);
784 
785     TRACE("(%p) refcount=%u\n", iface, ref);
786 
787     if (ref == 0) {
788         if (This->pStream) IStream_Release(This->pStream);
789         HeapFree(GetProcessHeap(), 0, This);
790     }
791     return ref;
792 }
793 
794 static HRESULT WINAPI IWICStreamImpl_Read(IWICStream *iface,
795     void *pv, ULONG cb, ULONG *pcbRead)
796 {
797     IWICStreamImpl *This = impl_from_IWICStream(iface);
798     TRACE("(%p): relay\n", This);
799 
800     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
801     return IStream_Read(This->pStream, pv, cb, pcbRead);
802 }
803 
804 static HRESULT WINAPI IWICStreamImpl_Write(IWICStream *iface,
805     void const *pv, ULONG cb, ULONG *pcbWritten)
806 {
807     IWICStreamImpl *This = impl_from_IWICStream(iface);
808     TRACE("(%p): relay\n", This);
809 
810     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
811     return IStream_Write(This->pStream, pv, cb, pcbWritten);
812 }
813 
814 static HRESULT WINAPI IWICStreamImpl_Seek(IWICStream *iface,
815     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
816 {
817     IWICStreamImpl *This = impl_from_IWICStream(iface);
818     TRACE("(%p): relay\n", This);
819 
820     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
821     return IStream_Seek(This->pStream, dlibMove, dwOrigin, plibNewPosition);
822 }
823 
824 static HRESULT WINAPI IWICStreamImpl_SetSize(IWICStream *iface,
825     ULARGE_INTEGER libNewSize)
826 {
827     IWICStreamImpl *This = impl_from_IWICStream(iface);
828     TRACE("(%p): relay\n", This);
829 
830     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
831     return IStream_SetSize(This->pStream, libNewSize);
832 }
833 
834 static HRESULT WINAPI IWICStreamImpl_CopyTo(IWICStream *iface,
835     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
836 {
837     IWICStreamImpl *This = impl_from_IWICStream(iface);
838     TRACE("(%p): relay\n", This);
839 
840     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
841     return IStream_CopyTo(This->pStream, pstm, cb, pcbRead, pcbWritten);
842 }
843 
844 static HRESULT WINAPI IWICStreamImpl_Commit(IWICStream *iface,
845     DWORD grfCommitFlags)
846 {
847     IWICStreamImpl *This = impl_from_IWICStream(iface);
848     TRACE("(%p): relay\n", This);
849 
850     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
851     return IStream_Commit(This->pStream, grfCommitFlags);
852 }
853 
854 static HRESULT WINAPI IWICStreamImpl_Revert(IWICStream *iface)
855 {
856     IWICStreamImpl *This = impl_from_IWICStream(iface);
857     TRACE("(%p): relay\n", This);
858 
859     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
860     return IStream_Revert(This->pStream);
861 }
862 
863 static HRESULT WINAPI IWICStreamImpl_LockRegion(IWICStream *iface,
864     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
865 {
866     IWICStreamImpl *This = impl_from_IWICStream(iface);
867     TRACE("(%p): relay\n", This);
868 
869     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
870     return IStream_LockRegion(This->pStream, libOffset, cb, dwLockType);
871 }
872 
873 static HRESULT WINAPI IWICStreamImpl_UnlockRegion(IWICStream *iface,
874     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
875 {
876     IWICStreamImpl *This = impl_from_IWICStream(iface);
877     TRACE("(%p): relay\n", This);
878 
879     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
880     return IStream_UnlockRegion(This->pStream, libOffset, cb, dwLockType);
881 }
882 
883 static HRESULT WINAPI IWICStreamImpl_Stat(IWICStream *iface,
884     STATSTG *pstatstg, DWORD grfStatFlag)
885 {
886     IWICStreamImpl *This = impl_from_IWICStream(iface);
887     TRACE("(%p): relay\n", This);
888 
889     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
890     return IStream_Stat(This->pStream, pstatstg, grfStatFlag);
891 }
892 
893 static HRESULT WINAPI IWICStreamImpl_Clone(IWICStream *iface,
894     IStream **ppstm)
895 {
896     IWICStreamImpl *This = impl_from_IWICStream(iface);
897     TRACE("(%p): relay\n", This);
898 
899     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
900     return IStream_Clone(This->pStream, ppstm);
901 }
902 
903 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface,
904     IStream *pIStream)
905 {
906     ULARGE_INTEGER offset, size;
907     TRACE("(%p): relay\n", iface);
908 
909     offset.QuadPart = 0;
910     size.u.LowPart = 0xffffffff;
911     size.u.HighPart = 0xffffffff;
912     return IWICStream_InitializeFromIStreamRegion(iface, pIStream, offset, size);
913 }
914 
915 static HRESULT WINAPI IWICStreamImpl_InitializeFromFilename(IWICStream *iface,
916     LPCWSTR wzFileName, DWORD dwDesiredAccess)
917 {
918     IWICStreamImpl *This = impl_from_IWICStream(iface);
919     HRESULT hr;
920     DWORD dwMode;
921     IStream *stream;
922 
923     TRACE("(%p, %s, %u)\n", iface, debugstr_w(wzFileName), dwDesiredAccess);
924 
925     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
926 
927     if(dwDesiredAccess & GENERIC_WRITE)
928         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
929     else if(dwDesiredAccess & GENERIC_READ)
930         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
931     else
932         return E_INVALIDARG;
933 
934     hr = SHCreateStreamOnFileW(wzFileName, dwMode, &stream);
935 
936     if (SUCCEEDED(hr))
937     {
938         if (InterlockedCompareExchangePointer((void**)&This->pStream, stream, NULL))
939         {
940             /* Some other thread set the stream first. */
941             IStream_Release(stream);
942             hr = WINCODEC_ERR_WRONGSTATE;
943         }
944     }
945 
946     return hr;
947 }
948 
949 /******************************************
950  * IWICStream_InitializeFromMemory
951  *
952  * Initializes the internal IStream object to retrieve its data from a memory chunk.
953  *
954  * PARAMS
955  *   pbBuffer     [I] pointer to the memory chunk
956  *   cbBufferSize [I] number of bytes to use from the memory chunk
957  *
958  * RETURNS
959  *   SUCCESS: S_OK
960  *   FAILURE: E_INVALIDARG, if pbBuffer is NULL
961  *            E_OUTOFMEMORY, if we run out of memory
962  *            WINCODEC_ERR_WRONGSTATE, if the IStream object has already been initialized before
963  *
964  */
965 static HRESULT WINAPI IWICStreamImpl_InitializeFromMemory(IWICStream *iface,
966     BYTE *pbBuffer, DWORD cbBufferSize)
967 {
968     IWICStreamImpl *This = impl_from_IWICStream(iface);
969     StreamOnMemory *pObject;
970     TRACE("(%p,%p)\n", iface, pbBuffer);
971 
972     if (!pbBuffer) return E_INVALIDARG;
973     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
974 
975     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnMemory));
976     if (!pObject) return E_OUTOFMEMORY;
977 
978     pObject->IStream_iface.lpVtbl = &StreamOnMemory_Vtbl;
979     pObject->ref = 1;
980     pObject->pbMemory = pbBuffer;
981     pObject->dwMemsize = cbBufferSize;
982     pObject->dwCurPos = 0;
983     InitializeCriticalSection(&pObject->lock);
984     pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnMemory.lock");
985 
986     if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL))
987     {
988         /* Some other thread set the stream first. */
989         IStream_Release(&pObject->IStream_iface);
990         return WINCODEC_ERR_WRONGSTATE;
991     }
992 
993     return S_OK;
994 }
995 
996 static HRESULT map_file(HANDLE file, HANDLE *map, void **mem, LARGE_INTEGER *size)
997 {
998     *map = NULL;
999     if (!GetFileSizeEx(file, size)) return HRESULT_FROM_WIN32(GetLastError());
1000     if (size->u.HighPart)
1001     {
1002         WARN("file too large\n");
1003         return E_FAIL;
1004     }
1005     if (!(*map = CreateFileMappingW(file, NULL, PAGE_READONLY, 0, size->u.LowPart, NULL)))
1006     {
1007         return HRESULT_FROM_WIN32(GetLastError());
1008     }
1009     if (!(*mem = MapViewOfFile(*map, FILE_MAP_READ, 0, 0, size->u.LowPart)))
1010     {
1011         CloseHandle(*map);
1012         return HRESULT_FROM_WIN32(GetLastError());
1013     }
1014     return S_OK;
1015 }
1016 
1017 HRESULT stream_initialize_from_filehandle(IWICStream *iface, HANDLE file)
1018 {
1019     IWICStreamImpl *This = impl_from_IWICStream(iface);
1020     StreamOnFileHandle *pObject;
1021     IWICStream *stream = NULL;
1022     HANDLE map;
1023     void *mem;
1024     LARGE_INTEGER size;
1025     HRESULT hr;
1026     TRACE("(%p,%p)\n", iface, file);
1027 
1028     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
1029 
1030     hr = map_file(file, &map, &mem, &size);
1031     if (FAILED(hr)) return hr;
1032 
1033     hr = StreamImpl_Create(&stream);
1034     if (FAILED(hr)) goto error;
1035 
1036     hr = IWICStreamImpl_InitializeFromMemory(stream, mem, size.u.LowPart);
1037     if (FAILED(hr)) goto error;
1038 
1039     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnFileHandle));
1040     if (!pObject)
1041     {
1042         hr = E_OUTOFMEMORY;
1043         goto error;
1044     }
1045     pObject->IStream_iface.lpVtbl = &StreamOnFileHandle_Vtbl;
1046     pObject->ref = 1;
1047     pObject->map = map;
1048     pObject->mem = mem;
1049     pObject->stream = stream;
1050 
1051     if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL))
1052     {
1053         /* Some other thread set the stream first. */
1054         IStream_Release(&pObject->IStream_iface);
1055         return WINCODEC_ERR_WRONGSTATE;
1056     }
1057     return S_OK;
1058 
1059 error:
1060     if (stream) IWICStream_Release(stream);
1061     UnmapViewOfFile(mem);
1062     CloseHandle(map);
1063     return hr;
1064 }
1065 
1066 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStreamRegion(IWICStream *iface,
1067     IStream *pIStream, ULARGE_INTEGER ulOffset, ULARGE_INTEGER ulMaxSize)
1068 {
1069     IWICStreamImpl *This = impl_from_IWICStream(iface);
1070     StreamOnStreamRange *pObject;
1071     TRACE("(%p,%p)\n", iface, pIStream);
1072 
1073     if (!pIStream) return E_INVALIDARG;
1074     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
1075 
1076     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnStreamRange));
1077     if (!pObject) return E_OUTOFMEMORY;
1078 
1079     pObject->IStream_iface.lpVtbl = &StreamOnStreamRange_Vtbl;
1080     pObject->ref = 1;
1081     IStream_AddRef(pIStream);
1082     pObject->stream = pIStream;
1083     pObject->pos.QuadPart = 0;
1084     pObject->offset = ulOffset;
1085     pObject->max_size = ulMaxSize;
1086     InitializeCriticalSection(&pObject->lock);
1087     pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnStreamRange.lock");
1088 
1089     if (InterlockedCompareExchangePointer((void**)&This->pStream, pObject, NULL))
1090     {
1091         /* Some other thread set the stream first. */
1092         IStream_Release(&pObject->IStream_iface);
1093         return WINCODEC_ERR_WRONGSTATE;
1094     }
1095 
1096     return S_OK;
1097 }
1098 
1099 
1100 static const IWICStreamVtbl WICStream_Vtbl =
1101 {
1102     /*** IUnknown methods ***/
1103     IWICStreamImpl_QueryInterface,
1104     IWICStreamImpl_AddRef,
1105     IWICStreamImpl_Release,
1106     /*** ISequentialStream methods ***/
1107     IWICStreamImpl_Read,
1108     IWICStreamImpl_Write,
1109     /*** IStream methods ***/
1110     IWICStreamImpl_Seek,
1111     IWICStreamImpl_SetSize,
1112     IWICStreamImpl_CopyTo,
1113     IWICStreamImpl_Commit,
1114     IWICStreamImpl_Revert,
1115     IWICStreamImpl_LockRegion,
1116     IWICStreamImpl_UnlockRegion,
1117     IWICStreamImpl_Stat,
1118     IWICStreamImpl_Clone,
1119     /*** IWICStream methods ***/
1120     IWICStreamImpl_InitializeFromIStream,
1121     IWICStreamImpl_InitializeFromFilename,
1122     IWICStreamImpl_InitializeFromMemory,
1123     IWICStreamImpl_InitializeFromIStreamRegion,
1124 };
1125 
1126 HRESULT StreamImpl_Create(IWICStream **stream)
1127 {
1128     IWICStreamImpl *pObject;
1129 
1130     if( !stream ) return E_INVALIDARG;
1131 
1132     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(IWICStreamImpl));
1133     if( !pObject ) {
1134         *stream = NULL;
1135         return E_OUTOFMEMORY;
1136     }
1137 
1138     pObject->IWICStream_iface.lpVtbl = &WICStream_Vtbl;
1139     pObject->ref = 1;
1140     pObject->pStream = NULL;
1141 
1142     *stream = &pObject->IWICStream_iface;
1143 
1144     return S_OK;
1145 }
1146