windowscodecs: Add locking to StreamOnMemory.
[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 "wincodec.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     const IStreamVtbl *lpVtbl;
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 HRESULT WINAPI StreamOnMemory_QueryInterface(IStream *iface,
49     REFIID iid, void **ppv)
50 {
51     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
52
53     if (!ppv) return E_INVALIDARG;
54
55     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
56         IsEqualIID(&IID_ISequentialStream, iid))
57     {
58         *ppv = iface;
59         IUnknown_AddRef((IUnknown*)*ppv);
60         return S_OK;
61     }
62     else
63     {
64         *ppv = NULL;
65         return E_NOINTERFACE;
66     }
67 }
68
69 static ULONG WINAPI StreamOnMemory_AddRef(IStream *iface)
70 {
71     StreamOnMemory *This = (StreamOnMemory*)iface;
72     ULONG ref = InterlockedIncrement(&This->ref);
73
74     TRACE("(%p) refcount=%u\n", iface, ref);
75
76     return ref;
77 }
78
79 static ULONG WINAPI StreamOnMemory_Release(IStream *iface)
80 {
81     StreamOnMemory *This = (StreamOnMemory*)iface;
82     ULONG ref = InterlockedDecrement(&This->ref);
83
84     TRACE("(%p) refcount=%u\n", iface, ref);
85
86     if (ref == 0) {
87         This->lock.DebugInfo->Spare[0] = 0;
88         DeleteCriticalSection(&This->lock);
89         HeapFree(GetProcessHeap(), 0, This);
90     }
91     return ref;
92 }
93
94 static HRESULT WINAPI StreamOnMemory_Read(IStream *iface,
95     void *pv, ULONG cb, ULONG *pcbRead)
96 {
97     StreamOnMemory *This = (StreamOnMemory*)iface;
98     ULONG uBytesRead;
99     TRACE("(%p)\n", This);
100
101     if (!pv) return E_INVALIDARG;
102
103     EnterCriticalSection(&This->lock);
104     uBytesRead = min(cb, This->dwMemsize - This->dwCurPos);
105     memcpy(pv, This->pbMemory + This->dwCurPos, uBytesRead);
106     This->dwCurPos += uBytesRead;
107     LeaveCriticalSection(&This->lock);
108
109     if (pcbRead) *pcbRead = uBytesRead;
110
111     return S_OK;
112 }
113
114 static HRESULT WINAPI StreamOnMemory_Write(IStream *iface,
115     void const *pv, ULONG cb, ULONG *pcbWritten)
116 {
117     StreamOnMemory *This = (StreamOnMemory*)iface;
118     HRESULT hr;
119     TRACE("(%p)\n", This);
120
121     if (!pv) return E_INVALIDARG;
122
123     EnterCriticalSection(&This->lock);
124     if (cb > This->dwMemsize - This->dwCurPos) {
125         hr = STG_E_MEDIUMFULL;
126     }
127     else {
128         if (cb) {
129             memcpy(This->pbMemory + This->dwCurPos, pv, cb);
130             This->dwCurPos += cb;
131             hr = S_OK;
132         }
133         if (pcbWritten) *pcbWritten = cb;
134     }
135     LeaveCriticalSection(&This->lock);
136
137     return hr;
138 }
139
140 static HRESULT WINAPI StreamOnMemory_Seek(IStream *iface,
141     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
142 {
143     StreamOnMemory *This = (StreamOnMemory*)iface;
144     LARGE_INTEGER NewPosition;
145     HRESULT hr=S_OK;
146     TRACE("(%p)\n", This);
147
148     EnterCriticalSection(&This->lock);
149     if (dwOrigin == STREAM_SEEK_SET) NewPosition.QuadPart = dlibMove.QuadPart;
150     else if (dwOrigin == STREAM_SEEK_CUR) NewPosition.QuadPart = This->dwCurPos + dlibMove.QuadPart;
151     else if (dwOrigin == STREAM_SEEK_END) NewPosition.QuadPart = This->dwMemsize + dlibMove.QuadPart;
152     else hr = E_INVALIDARG;
153
154     if (SUCCEEDED(hr)) {
155         if (NewPosition.u.HighPart) hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
156         else if (NewPosition.QuadPart > This->dwMemsize) hr = E_INVALIDARG;
157         else if (NewPosition.QuadPart < 0) hr = E_INVALIDARG;
158     }
159
160     if (SUCCEEDED(hr)) {
161         This->dwCurPos = NewPosition.u.LowPart;
162
163         if(plibNewPosition) plibNewPosition->QuadPart = This->dwCurPos;
164     }
165     LeaveCriticalSection(&This->lock);
166
167     return hr;
168 }
169
170 /* SetSize isn't implemented in the native windowscodecs DLL either */
171 static HRESULT WINAPI StreamOnMemory_SetSize(IStream *iface,
172     ULARGE_INTEGER libNewSize)
173 {
174     TRACE("(%p)\n", iface);
175     return E_NOTIMPL;
176 }
177
178 /* CopyTo isn't implemented in the native windowscodecs DLL either */
179 static HRESULT WINAPI StreamOnMemory_CopyTo(IStream *iface,
180     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
181 {
182     TRACE("(%p)\n", iface);
183     return E_NOTIMPL;
184 }
185
186 /* Commit isn't implemented in the native windowscodecs DLL either */
187 static HRESULT WINAPI StreamOnMemory_Commit(IStream *iface,
188     DWORD grfCommitFlags)
189 {
190     TRACE("(%p)\n", iface);
191     return E_NOTIMPL;
192 }
193
194 /* Revert isn't implemented in the native windowscodecs DLL either */
195 static HRESULT WINAPI StreamOnMemory_Revert(IStream *iface)
196 {
197     TRACE("(%p)\n", iface);
198     return E_NOTIMPL;
199 }
200
201 /* LockRegion isn't implemented in the native windowscodecs DLL either */
202 static HRESULT WINAPI StreamOnMemory_LockRegion(IStream *iface,
203     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
204 {
205     TRACE("(%p)\n", iface);
206     return E_NOTIMPL;
207 }
208
209 /* UnlockRegion isn't implemented in the native windowscodecs DLL either */
210 static HRESULT WINAPI StreamOnMemory_UnlockRegion(IStream *iface,
211     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
212 {
213     TRACE("(%p)\n", iface);
214     return E_NOTIMPL;
215 }
216
217 static HRESULT WINAPI StreamOnMemory_Stat(IStream *iface,
218     STATSTG *pstatstg, DWORD grfStatFlag)
219 {
220     StreamOnMemory *This = (StreamOnMemory*)iface;
221     TRACE("(%p)\n", This);
222
223     if (!pstatstg) return E_INVALIDARG;
224
225     ZeroMemory(pstatstg, sizeof(STATSTG));
226     pstatstg->type = STGTY_STREAM;
227     pstatstg->cbSize.QuadPart = This->dwMemsize;
228
229     return S_OK;
230 }
231
232 /* Clone isn't implemented in the native windowscodecs DLL either */
233 static HRESULT WINAPI StreamOnMemory_Clone(IStream *iface,
234     IStream **ppstm)
235 {
236     TRACE("(%p)\n", iface);
237     return E_NOTIMPL;
238 }
239
240
241 const IStreamVtbl StreamOnMemory_Vtbl =
242 {
243     /*** IUnknown methods ***/
244     StreamOnMemory_QueryInterface,
245     StreamOnMemory_AddRef,
246     StreamOnMemory_Release,
247     /*** ISequentialStream methods ***/
248     StreamOnMemory_Read,
249     StreamOnMemory_Write,
250     /*** IStream methods ***/
251     StreamOnMemory_Seek,
252     StreamOnMemory_SetSize,
253     StreamOnMemory_CopyTo,
254     StreamOnMemory_Commit,
255     StreamOnMemory_Revert,
256     StreamOnMemory_LockRegion,
257     StreamOnMemory_UnlockRegion,
258     StreamOnMemory_Stat,
259     StreamOnMemory_Clone,
260 };
261
262 /******************************************
263  * IWICStream implementation
264  *
265  */
266 typedef struct IWICStreamImpl
267 {
268     const IWICStreamVtbl *lpVtbl;
269     LONG ref;
270
271     IStream *pStream;
272 } IWICStreamImpl;
273
274 static HRESULT WINAPI IWICStreamImpl_QueryInterface(IWICStream *iface,
275     REFIID iid, void **ppv)
276 {
277     IWICStreamImpl *This = (IWICStreamImpl*)iface;
278     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(iid), ppv);
279
280     if (!ppv) return E_INVALIDARG;
281
282     if (IsEqualIID(&IID_IUnknown, iid) || IsEqualIID(&IID_IStream, iid) ||
283         IsEqualIID(&IID_ISequentialStream, iid) || IsEqualIID(&IID_IWICStream, iid))
284     {
285         *ppv = This;
286         IUnknown_AddRef((IUnknown*)*ppv);
287         return S_OK;
288     }
289     else
290     {
291         *ppv = NULL;
292         return E_NOINTERFACE;
293     }
294 }
295
296 static ULONG WINAPI IWICStreamImpl_AddRef(IWICStream *iface)
297 {
298     IWICStreamImpl *This = (IWICStreamImpl*)iface;
299     ULONG ref = InterlockedIncrement(&This->ref);
300
301     TRACE("(%p) refcount=%u\n", iface, ref);
302
303     return ref;
304 }
305
306 static ULONG WINAPI IWICStreamImpl_Release(IWICStream *iface)
307 {
308     IWICStreamImpl *This = (IWICStreamImpl*)iface;
309     ULONG ref = InterlockedDecrement(&This->ref);
310
311     TRACE("(%p) refcount=%u\n", iface, ref);
312
313     if (ref == 0) {
314         if (This->pStream) IStream_Release(This->pStream);
315         HeapFree(GetProcessHeap(), 0, This);
316     }
317     return ref;
318 }
319
320 static HRESULT WINAPI IWICStreamImpl_Read(IWICStream *iface,
321     void *pv, ULONG cb, ULONG *pcbRead)
322 {
323     IWICStreamImpl *This = (IWICStreamImpl*)iface;
324     TRACE("(%p): relay\n", This);
325
326     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
327     return IStream_Read(This->pStream, pv, cb, pcbRead);
328 }
329
330 static HRESULT WINAPI IWICStreamImpl_Write(IWICStream *iface,
331     void const *pv, ULONG cb, ULONG *pcbWritten)
332 {
333     IWICStreamImpl *This = (IWICStreamImpl*)iface;
334     TRACE("(%p): relay\n", This);
335
336     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
337     return IStream_Write(This->pStream, pv, cb, pcbWritten);
338 }
339
340 static HRESULT WINAPI IWICStreamImpl_Seek(IWICStream *iface,
341     LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
342 {
343     IWICStreamImpl *This = (IWICStreamImpl*)iface;
344     TRACE("(%p): relay\n", This);
345
346     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
347     return IStream_Seek(This->pStream, dlibMove, dwOrigin, plibNewPosition);
348 }
349
350 static HRESULT WINAPI IWICStreamImpl_SetSize(IWICStream *iface,
351     ULARGE_INTEGER libNewSize)
352 {
353     IWICStreamImpl *This = (IWICStreamImpl*)iface;
354     TRACE("(%p): relay\n", This);
355
356     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
357     return IStream_SetSize(This->pStream, libNewSize);
358 }
359
360 static HRESULT WINAPI IWICStreamImpl_CopyTo(IWICStream *iface,
361     IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
362 {
363     IWICStreamImpl *This = (IWICStreamImpl*)iface;
364     TRACE("(%p): relay\n", This);
365
366     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
367     return IStream_CopyTo(This->pStream, pstm, cb, pcbRead, pcbWritten);
368 }
369
370 static HRESULT WINAPI IWICStreamImpl_Commit(IWICStream *iface,
371     DWORD grfCommitFlags)
372 {
373     IWICStreamImpl *This = (IWICStreamImpl*)iface;
374     TRACE("(%p): relay\n", This);
375
376     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
377     return IStream_Commit(This->pStream, grfCommitFlags);
378 }
379
380 static HRESULT WINAPI IWICStreamImpl_Revert(IWICStream *iface)
381 {
382     IWICStreamImpl *This = (IWICStreamImpl*)iface;
383     TRACE("(%p): relay\n", This);
384
385     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
386     return IStream_Revert(This->pStream);
387 }
388
389 static HRESULT WINAPI IWICStreamImpl_LockRegion(IWICStream *iface,
390     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
391 {
392     IWICStreamImpl *This = (IWICStreamImpl*)iface;
393     TRACE("(%p): relay\n", This);
394
395     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
396     return IStream_LockRegion(This->pStream, libOffset, cb, dwLockType);
397 }
398
399 static HRESULT WINAPI IWICStreamImpl_UnlockRegion(IWICStream *iface,
400     ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
401 {
402     IWICStreamImpl *This = (IWICStreamImpl*)iface;
403     TRACE("(%p): relay\n", This);
404
405     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
406     return IStream_UnlockRegion(This->pStream, libOffset, cb, dwLockType);
407 }
408
409 static HRESULT WINAPI IWICStreamImpl_Stat(IWICStream *iface,
410     STATSTG *pstatstg, DWORD grfStatFlag)
411 {
412     IWICStreamImpl *This = (IWICStreamImpl*)iface;
413     TRACE("(%p): relay\n", This);
414
415     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
416     return IStream_Stat(This->pStream, pstatstg, grfStatFlag);
417 }
418
419 static HRESULT WINAPI IWICStreamImpl_Clone(IWICStream *iface,
420     IStream **ppstm)
421 {
422     IWICStreamImpl *This = (IWICStreamImpl*)iface;
423     TRACE("(%p): relay\n", This);
424
425     if (!This->pStream) return WINCODEC_ERR_NOTINITIALIZED;
426     return IStream_Clone(This->pStream, ppstm);
427 }
428
429 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStream(IWICStream *iface,
430     IStream *pIStream)
431 {
432     FIXME("(%p): stub\n", iface);
433     return E_NOTIMPL;
434 }
435
436 static HRESULT WINAPI IWICStreamImpl_InitializeFromFilename(IWICStream *iface,
437     LPCWSTR wzFileName, DWORD dwDesiredAccess)
438 {
439     FIXME("(%p): stub\n", iface);
440     return E_NOTIMPL;
441 }
442
443 /******************************************
444  * IWICStream_InitializeFromMemory
445  *
446  * Initializes the internal IStream object to retrieve its data from a memory chunk.
447  *
448  * PARAMS
449  *   pbBuffer     [I] pointer to the memory chunk
450  *   cbBufferSize [I] number of bytes to use from the memory chunk
451  *
452  * RETURNS
453  *   SUCCESS: S_OK
454  *   FAILURE: E_INVALIDARG, if pbBuffer is NULL
455  *            E_OUTOFMEMORY, if we run out of memory
456  *            WINCODEC_ERR_WRONGSTATE, if the IStream object has already been initialized before
457  *
458  */
459 static HRESULT WINAPI IWICStreamImpl_InitializeFromMemory(IWICStream *iface,
460     BYTE *pbBuffer, DWORD cbBufferSize)
461 {
462     IWICStreamImpl *This = (IWICStreamImpl*)iface;
463     StreamOnMemory *pObject;
464     TRACE("(%p,%p)\n", iface, pbBuffer);
465
466     if (!pbBuffer) return E_INVALIDARG;
467     if (This->pStream) return WINCODEC_ERR_WRONGSTATE;
468
469     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(StreamOnMemory));
470     if (!pObject) return E_OUTOFMEMORY;
471
472     pObject->lpVtbl = &StreamOnMemory_Vtbl;
473     pObject->ref = 1;
474     pObject->pbMemory = pbBuffer;
475     pObject->dwMemsize = cbBufferSize;
476     pObject->dwCurPos = 0;
477     InitializeCriticalSection(&pObject->lock);
478     pObject->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": StreamOnMemory.lock");
479
480     This->pStream = (IStream*)pObject;
481     return S_OK;
482 }
483
484 static HRESULT WINAPI IWICStreamImpl_InitializeFromIStreamRegion(IWICStream *iface,
485     IStream *pIStream, ULARGE_INTEGER ulOffset, ULARGE_INTEGER ulMaxSize)
486 {
487     FIXME("(%p): stub\n", iface);
488     return E_NOTIMPL;
489 }
490
491
492 const IWICStreamVtbl WICStream_Vtbl =
493 {
494     /*** IUnknown methods ***/
495     IWICStreamImpl_QueryInterface,
496     IWICStreamImpl_AddRef,
497     IWICStreamImpl_Release,
498     /*** ISequentialStream methods ***/
499     IWICStreamImpl_Read,
500     IWICStreamImpl_Write,
501     /*** IStream methods ***/
502     IWICStreamImpl_Seek,
503     IWICStreamImpl_SetSize,
504     IWICStreamImpl_CopyTo,
505     IWICStreamImpl_Commit,
506     IWICStreamImpl_Revert,
507     IWICStreamImpl_LockRegion,
508     IWICStreamImpl_UnlockRegion,
509     IWICStreamImpl_Stat,
510     IWICStreamImpl_Clone,
511     /*** IWICStream methods ***/
512     IWICStreamImpl_InitializeFromIStream,
513     IWICStreamImpl_InitializeFromFilename,
514     IWICStreamImpl_InitializeFromMemory,
515     IWICStreamImpl_InitializeFromIStreamRegion,
516 };
517
518 HRESULT StreamImpl_Create(IWICStream **stream)
519 {
520     IWICStreamImpl *pObject;
521
522     if( !stream ) return E_INVALIDARG;
523
524     pObject = HeapAlloc(GetProcessHeap(), 0, sizeof(IWICStreamImpl));
525     if( !pObject ) {
526         *stream = NULL;
527         return E_OUTOFMEMORY;
528     }
529
530     pObject->lpVtbl = &WICStream_Vtbl;
531     pObject->ref = 1;
532     pObject->pStream = NULL;
533
534     *stream = (IWICStream*)pObject;
535
536     return S_OK;
537 }